Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

interfaces to fix inheritence mess?

I'm working on a project with the following (very simplified) structure:

BaseClass

SubClassA : BaseClass
SubClassB : BaseClass

There is a UI (with a lot of logic) which uses SubClassA, and then saves it to another component which takes BaseClass as a parameter but immediately casts the argument to SubClassB. This fails as the UI is passing in SubClassA.

UI:

MyComponent.Save(subClassA)

Component:
Save(BaseClass baseClass)
{
SubClassB subClassB = (SubClassB)baseClass;

...

the current implementation creates an instance of SubClassB in the UI and pass that across - but it leads to lots of code such as

SubClassB.Property1 = SubClassA.Property1a

I'm contemplating creating a common interface which the 2 sub classes would implement. It would be a lot of work but slowly I think I could flatten the current very deep hierarchy. Reworking either the UI or the component to use the other sub type would be just as much work as the structures are different (though many fields map). Is the interface approach the right way to go? I feel there might be something I'm missing.

like image 774
NDJ Avatar asked Jan 29 '26 20:01

NDJ


2 Answers

If SubclassA and SubclassB are related only by their ability to Save, then yes, BaseClass would be better as an interface that both sub-classes implement.

It won't solve your immediate problem straight away: the component casting from base class to (the wrong) derived class. It looks like there could be several levels of refactoring to do here. Patching up the code so that the component casting to a SubclassA by making one for it to use is wasteful, I think. Changing the component so it can operate on a single common type would be a big win there.

Flattening a deep hierarchy would bring lots of other benefits, too - like making it simpler. If there end up being a few interfaces that they all implement, that's not necessarily a bad thing. Beware of lots of interface types hunting in packs, however.

In short, reworking both the UI and the component - and any other code, too - to work in terms of just a small number of interfaces, with no knowledge of the implementing classes, will pay dividends.

like image 169
SteveLove Avatar answered Jan 31 '26 12:01

SteveLove


From a consumer standpoint, interfaces can do almost everything that abstract classes can do (the main exceptions being that a class can expose a field as a byref, while interfaces have no means of doing so, and that static members associated with a class can be grouped under the class name, static members related to an interface must be grouped under a different name). Except in those rare cases where it's necessary to expose a field as a byref, the primary advantage of an abstract class comes on the side of the implementation. All of the functionality associated with an interface must be provided separately in every class which implements it, even when such functionality is common to 99% of the classes which implement it. By contrast, if 99% of the concrete classes derived from an abstract class will implement a particular method the same way, it's possible for the abstract class to define that method once and let derived classes inherit it without having to pay it any heed whatsoever. Such an advantage can be nice, but there's a major catch: a class can only inherit functionality from one other class. Since interfaces don't include any functionality, they can be inherited from any number of other classes.

When one is defining an abstract class, I would suggest that one should in many cases also define an interface which includes the same public functionality (which the abstract class should implement), and avoid using variables or parameters of the abstract class type. This will allow implementations which can inherit from the abstract class to achieve the ease-of-implementation benefits that would come from doing so, but will also make it possible to define implementations which inherit from something else. Writing an implementation which inherits from some other type would be more work, but if code never uses the abstract-class type in variable, field, or parameter declarations, code which uses derivatives of the abstract class would work just as well with interface implementations that don't.

like image 20
supercat Avatar answered Jan 31 '26 12:01

supercat