Note: See the update below, as I altered the original concept, based on the first answer+comment
I'm sorry if this is a trivial question or if it solely depends on the programmers preference. As I am not sure about real advantages or disadvantages concerning e. g. clarity or KISS, I ask this question.
In an abstract class concept where should I place the actual objects?
(should I prefer BaseObject
or DerivedPrivateObject
)
public abstract class BaseClass<TBase>
{
// Should we manage the generic object in the base class?
protected TBase BaseObject = null;
// Or should we access it via abstract property?
protected abstract TBase DerivedObject { get; }
// The derived implementation has to create the object
protected abstract TBase CreateObject();
public void DoSomethingGeneric()
{
// Base may directly use its base object
BaseObject.GenericMethod();
// Otherwise it has to access the derived property every time
DerivedObject.GenericMethod();
}
}
public class DerivedClass : BaseClass<TSpecialized> where TSpecialized: TBase
{
// Should we manage the specialized object here in the derived class?
private TSpecialized DerivedPrivateObject = null;
protected TBase DerivedObject
{
get
{
return DerivedPrivateObject;
}
}
protected TBase CreateObject()
{
DerivedPrivateObject = new DerivedObject();
// We may additionally store this into the base object, but isn't this ugly?
BaseObject = DerivedPrivateObject;
return DerivedPrivateObject;
}
public void DoSomething()
{
// We can directly use the derived object type
DerivedPrivateObject.SpecializedMethodNotInBaseType();
// Otherwise we would have to convert
(BaseObject as TSpecialized).SpecializedMethodNotInBaseType();
}
}
Update (3) after the answer+comment of David Culp
Note to update 3: As I mentioned, I tried to simplify the classes to focus on the actual question. But as both commentators correctly stated, I failed with this. The simplification led to incorrect code and an incorrect concept. Because of this, I extended the below code structure to the real implementation and I hope this still is comprehensible. I am really sorry for any confusions that were provoked by the faulty simplification!
The references to LSP and DRY by David Culp allowed me to better understand my own question. This not only destroyed my doubts of having exagerated peanuts.
But also, this allowed me to come up with an acceptable solution:
public abstract class BaseClass<TBase> : ISomeInterface where TBase : class
{
// We manage the generic object in the base class for allowing generic usage
protected OtherBaseClass<TBase> BaseObject = null;
// The derived implementation has to create the object
protected abstract OtherBaseClass<TBase> CreateObject();
public void Initialize()
{
// Let the derived class create the specialized object, but store it back into here, for generic usage
BaseObject = CreateObject();
}
public void DoSomethingGeneric()
{
// Base may directly use the base object
BaseObject.GenericMethod();
}
}
public class DerivedClass : BaseClass<ISpecialized>
{
private SpecializedType DerivedObjectAccessor
{
get
{
return BaseObject as SpecializedType;
}
}
protected override OtherBaseClass<ISpecialized> CreateObject()
{
// Create the derived object, but let base class handle the assignment to BaseObject, for clarity reasons
return new SpecializedType();
}
public void DoSomething()
{
// We may use specialized stuff via local accessor property
DerivedObjectAccessor.SpecializedMethodNotInBaseType();
}
}
Just for clarification:
public class SpecializedType : OtherBaseClass<ISpecialized>, ISpecialized
{
// foreign implementation
}
Is this an acceptable solution concerning clarity and style?
Are there arguments against protected abstract void CreateObject();
not returning the object, letting the derived class assign it to BaseObject
(error prone?) - I want to leave it open whether CreateObject()
is called in a constructor already or via publicily available create method, but I won't omit the abstract method to let the derived logic know that is has to implement this.
(Update 2: as the above was horrible, I altered the create object handling)
The main point:
Are there arguments against the principle of using a property (DerivedObjectAccessor
) for local converted access to the generic base object?