9
votes

For example I have the following classes:

1.

class MyClass1
{
    public MyClass1 Method()
    {
        ...
        return new MyClass1();
    }
}

class MyClass2
{
    public MyClass2 Method()
    {
        ...
        return new MyClass2();
    }
}

The methods have the same bodies that's why I want to extract the code and re-use.

2.

abstract class MyClass
{

    protected void Method()
    {
        ...
    }
}

class MyClass1 : MyClass
{

    public MyClass1 Method()
    {
        base.Method();
        return new MyClass1();
    }
}

class MyClass2 : MyClass
{
    public MyClass2 Method()
    {
        base.Method();
        return new MyClass2();
    }
}

However since there are a lot of such methods it will be better to move the methods into the base class MyClass at all:

3.

abstract class MyClass<T>: where T : MyClass<T>
{
    protected abstract T Create();

    public T Method()
    {
        ...
        return Create();
    }
}

class MyClass1 : MyClass<MyClass1>
{
    protected override MyClass1 Create() => new MyClass1();
}

class MyClass2 : MyClass<MyClass2>
{
    protected override MyClass2 Create() => new MyClass2();
}

It works fine but the contract looks too weird. Of course, I can extract something like IMyInterface and return it instead of the class. However I have to preserve the original contract because it contains specific methods as well.

Upd: So, the weird thing is in bold - class MyClass1: MyClass<MyClass1>

3
“However I have to preserve the original contract” What do you mean with that?poke
Foo : Thing<Foo> is a fairly common pattern where Thing<T> needs to return instances of its subclass. I wouldn't worry about it.canton7
The pattern name is Curiously recurring template pattern. You can search the pattern name and find some articles like this, this one or this one or this one which describe/encourage/discourage using the pattern.Reza Aghaei

3 Answers

4
votes

This is the usual so-called self-type problem (you have Method() should return the same type as the object on which it was called). Your solution #3 looks a lot like F-bounded quantification. However, this is C#, not Java, so we can do a bit better using an extension class.

You can make sure those methods only get called on subclasses of MyClass by adding a where T : MyClass bound on T.

// Put all your shared methods in generic classes in here.
public static class MyMethods
{
    public static T Method<T>(this T x) where T : MyClass
    {
        ...
    }
}

Your classes don't change much, except they won't need to mention Method (or the other shared methods) at all.

public abstract class MyClass
{
    ...
}

public class MyClass1 : MyClass
{
    ...
}

public class MyClass2 : MyClass
{
    ...
}
3
votes

Yes it looks a little bit weird to have a method which only create.

Because you have a 2 classes MyClass1 and MyClass2 which have their specific different methods and only base method is common (which you put in base class) I think you can use Abstract factory pattern.

public class ClassFactory: IClassFactory
{
    public MyClass Create()
    {
         if (some condition)
              return new MyClass1;
         return new MyClass2;
    }
}

class MyClass
{
    protected string CommonLogic()
    {
         //common logic 
         return string;
    }

}

class MyClass1 : MyClass
{
    public object SpecificMethod()
    {
        CommonLogic();
        .....
    }

}

class MyClass2 : MyClass
{
    public object SpecificMethod2()
    {
        CommonLogic();
        .....
    }
}

In this case you won't have duplicated code and you will have some class which one will have responsibility about creating you classes and will know when and which class return. + You will easy use it IoC here.

I hope my answer will help you.

0
votes

There are some rare situations where a self-referencing type constraint may be needed, but I am not convinced that this is one of them.

It seems that you want to use the factory (Create) pattern and also have those factories return different concrete types. But at the same time you are saying that these concrete types all have something in common, specified by the base class.

The conventional approach would be to define the common features in an interface (IMyInterface as you suggested) and return that from the Create method. This would capture the polymorphic aspect of the concrete classes. Then the question is how to capture the other methods that are implemented in the concrete types. For this one can simply define additional interfaces that capture the various clusters of functionality implemented by more than one of the concrete classes.

To the extent that there are any dribs and drabs of functionality left over after you've done that, I would say handle them by casting would be the easiest -- at that point the functionality would be unique to just one of the concrete classes. If you want to fly with your eyes closed you could use the 'dynamic' type instead of casting.

Also, normally the Create method is not defined in the object instances, in other words normally objects are not their own factories. Typically they are either static or in a separate factory classes. In the current situation a bit of reflection helps deal with the fact that you have multiple derived types. There are various ways to do this besides what I show below.

So … perhaps something like this:

public interface ICommonFunctionality
{
    void SomethingThatEveryoneCanDo();
    // ... other common functionality
}

public interface IAdditionalFunctionality1
{
    void SomethingThatAFewCanDo();
    // ... other special functionality
}
public interface IAdditionalFunctionality2
{
    void SomethingThatOthersCanDo();
    // ... other special functionality
}

public class MyClass : ICommonFunctionality
{
    static public ICommonFunctionality Create(Type derivedType)
    {
        if (!typeof(ICommonFunctionality).IsAssignableFrom(derivedType)) { throw new ArgumentException(); }
        return derivedType.CreateInstance() as ICommonFunctionality;
    }

    virtual public void SomethingThatEveryoneCanDo() { /* ... */  }
}

public class MyClass1 : MyClass, IAdditionalFunctionality1
{
    public void SomethingThatAFewCanDo() { /* ... */ }
}

public class MyClass2 : MyClass, IAdditionalFunctionality1, IAdditionalFunctionality2
{
    public void SomethingThatAFewCanDo() { /* ... */ }
    public void SomethingThatOthersCanDo() { /* ... */ }
}

public class MyClass3 : MyClass, IAdditionalFunctionality2
{
    public void SomethingThatOthersCanDo() { /* ... */ }
}

public static class TypeHelpers
{
    public static object CreateInstance(this Type type, bool required = true)
    {
        System.Reflection.ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
        if (required && ctor == null) { throw new InvalidOperationException("Missing required constructor."); }
        return ctor?.Invoke(null);
    }
}

P.S. I have made the base class method virtual, this is pretty much optional depending on your situation.