简体   繁体   中英

Difference between abstract class whose constructor requires arguments, and abstract class with abstract get-only properties

public abstract class BaseProcessor
{

    public abstract void Initialize();

    private readonly string _executerPluginName;
    private readonly ILogService _logService;

    public BaseProcessor(string executerPluginName, ILogService logService)
    {
        this._executerPluginName = executerPluginName;
        this._logService = logService;
    }

    protected void CallExecutor()
    {
        this.Initialize();
        //common logic
    }
}

public class ConcreteProcessor : BaseProcessor
{

    public override void Initialize()
    {
        //concrete logic
    }

    public ConcreteProcessor(string executerPluginName, ILogService logService) : base(executerPluginName, logService)
    {
    }
}

AND

public abstract class BaseProcessor
{

    public abstract void Initialize();

    protected abstract string ExecuterPluginName { get; }
    protected abstract ILogService LogService { get; }

    protected void CallExecutor()
    {
        this.Initialize();
        //common logic
    }
}

public class ConcreteProcessor : BaseProcessor
{
    protected override string ExecuterPluginName { get{ throw new NotImplementedException(); } }
    protected override ILogService LogService { get{ throw new NotImplementedException(); } }

    public override void Initialize()
    {
        //concrete logic
    }

}

What kind of inheritance more preferable ? I'm going to use second solution, but i have some doubts about quantity of abstraction. Can you give some explanation about these approaches?

constructor parameters are always am become mandatory field for your code. making it property, makes it optional and can confuse client either to use. If those are not used, then it may cause error in run time.

If you are going with second approach, I would prefer interface, instead of abstract class.

One difference is that in the first example (base class constructor requires arguments), the value must be specified (by the inheritor) before the instance is fully constructed.

In the second example (base class has abstract get -only properties) it is easier for the concrete implementation to provide a more complex "calculation" of the return values, and they might probably assume that the current instance is already properly constructed (and take advantage of that).

I'm not sure what the context of the problem you applying the listed approaches, so I am somewhat stabbing in the dark with my response. Never the less, here is my 2 cents.

This first approach allows you to use the ILogService and ExecuterPluginName in the methods of your base class. This can come handy especially with the common activities such as logging.

I prefer this approach over the second approach for that reason alone.

However, if you do not have any common logic that uses shared resources, then holding it in the base class makes little sense. YAGNI applies here. In fact I might go so far to say, a base abstract class makes no sense if there is no common logic.

Instead using an interface might the be ticket, if all you are attempting to do is to force the concrete classes to adhere to a contract of methods and properties in their implementations.

Furthermore, the use of throw new NotImplementedException(); definitely is a code smell.

A side note: you can utilise dependency injection (via DI frameworks like Autofac etc.) to control the lifetime of objects passed into your concrete class. Ie you can make your logging service a singleton, and pass the same instance of it to all your implementations.

Consider the following three implementations of a "2d matrix" abstract class:

abstract public class Matrix2dv1
{
    double[,] data;
    protected Matrix2dv1(double[,] source)
    {
        data = (double[,])source.Clone();
    }
    public double this[int row,int column]
    {
        get { return data[row, column]; }
    }
}
abstract public class Matrix2dv2
{
    abstract public double this[int row, int column] { get; }
}
abstract public class Matrix2dv3
{
    public double this[int row, int column]
    {
        get { return getRowColumn(row,column); }
    }
    protected abstract double getRowColumn(int row, int column); 
}

The first version of the class takes care of a little more work on behalf of derivative classes, but it requires that every implementation make use of a double[,] as a backing store. If the indexed getter were virtual, derived classes could pass a dummy array (or even null ) to the constructor and use their own backing store, but in many cases they'd still end up wasting, at minimum, the storage used to hold the base class data field.

The second version of the class would require clients to do a little work, but would allow for the possibility that a type like IdentityMatrix might not need an array as a backing store (it could simply define its indexed getter to return 1.0 when row and column are equal, and 0.0 otherwise). Unfortunately, it wouldn't work well if the base class needs to support both mutable and immutable subtypes, since there's no way a derived class could both override the base class property getter and also define a read-write property.

The third version of the class avoids the problem of the second version by having a concrete non-virtual property getter which does nothing except chain to a virtual method. A derived class would have no problem both overriding that method and defining a new non-virtual read-write property whose getter chains to that same method and whose setter chains to a different virtual method.

The question of whether properties should be implemented in the base class will often depend upon whether there's a realistic possibility that some derived classes might want to have a different form of backing store than would be typical of most and might, as a consequence, have no use for backing-store fields of the most common type. Note that if 90% of derived classes would benefit from a field and code to go with it, but a few wouldn't, it may be helpful to have an "intermediate-level" abstract class (eg ArrayBackedMatrix ) which derives from the base but includes the common fields.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM