Aleksei Petrov
Aleksei Petrov

Reputation: 1099

Dealing with inheritance hierarchy because of Open-Closed Principle

When I try to follow the Open-Closed Principle (OCP), after number of implemented use cases I always end up with hierarchy of inherited classes. Usually it happens with ViewModels in MVVM structure, because they are being changed a lot. For example (C#, but could be any other class oriented language):

internal class MyAwesomeViewModel
{
    private IDependency1 _dependency1;

    public string Property1 { get; set; }
    public ICommand Command1 { get; }

    public MyAwesomeViewModel(IDependency1 dependency1)
    {
        _dependency1 = dependency1;
        Command1 = new DelegateCommand(...);
    }
}

internal class MyAwesomeViewModelWithAnotherProperty: MyAwesomeViewModel
{
    public string Property2 { get; set; }

    public MyAwesomeViewModelWithAnotherProperty(IDependency1 dependency1)
        :base(dependency1)
    {
    }
}

internal class MyAwesomeViewModelWithNewCommandAndDependency: MyAwesomeViewModelWithAnotherProperty
{
    private IDependency2 _dependency2;

    public ICommand Command2;

    public MyAwesomeViewModelWithNewCommandAndDependency(IDependency1 dependency1, IDependency2 dependency2)
    : base(dependency1)
    {
        _dependency2 = dependency2;
        Command2 = new DelegateCommand(...);
    }
}

internal class MyAwesomeViewModelWithBunchNewProperties : MyAwesomeViewModelWithNewCommandAndDependency
{
    public int IntProperty { get; }
    public bool BoolProperty { get; }
    public double DoubleProperty { get; }

    public MyAwesomeViewModelWithBunchNewProperties(IDependency1 dependency1, IDependency2 dependency2)
    : base(dependency1, dependency2)
    {
    }
}

The problem is, when it comes to inheritance depth of 4 and more levels it becomes very messy and difficult to read. Also I always encounter to naming problem, which is signal of wrong composition (like MainWindowViewModel, then MainWindowViewModelCloseCommand, then MainWindowViewModelUserRelatedProperties and so on).

Since only the last derived class is used (MyAwesomeViewModelWithBunchNewProperties in the example above), sometimes I consider to squash all the inheritance before release or other significant milestone into 1 class, like this:

internal class MyAwesomeViewModel
{
    public int IntProperty { get; }
    public bool BoolProperty { get; }
    public double DoubleProperty { get; }
    public string Property1 { get; set; }
    public string Property2 { get; set; }
    public ICommand Command1 { get; }
    public ICommand Command2;

    public MyAwesomeViewModelWithBunchNewProperties(IDependency1 dependency1, IDependency2 dependency2)
    {
        _dependency1 = dependency1;
        _dependency2 = dependency2;
        Command1 = new DelegateCommand(...);
        Command2 = new DelegateCommand(...);
    }
}

But it could violate Single Responsibility Principle (SRP) and lead to very large SuperClass.

The question: How to deal with lot of inheritance problem? Or it is not problem at all and it's OK to have this type of classes structure?

Upvotes: 2

Views: 492

Answers (3)

Marcel De Villiers
Marcel De Villiers

Reputation: 1822

Composition over inheritance!

A lot of times developers look at principles like SOLID and forget the basic Composition over inheritance principle.

The first thing to remember is that inheritance tightly bounds you to the base class. This leads to issues such as refused bequests (breaking the Liskov substitution principle).

When we speak of classes in OOP, we define behavior associated with data, not objects. When we model the problem in terms of the behavior it is trying to achieve, we can get small building blocks.

You can define the core behavior from MyAwesomeViewModel into tiny classes you can reference to it in your other classes. This way you can easily compose objects like MyAwesomeViewModelWithBunchNewProperties.

Regarding the Single responsibility principle, it is a very misunderstood principle. SRP states that behavior that lives together should change together. This means a single set of behaviors that are dependent on each other, and will change together belong in the same class.

Regarding your specific scenario, view models often might not benefit from composition or inheritance. View Models are data transfer objects(DTO's), they do not capture behavior. Code duplication here can be very easily overlooked\acceptable. If code duplication poses a problem in your view models, just compose them from other DTO's

Upvotes: 2

usr
usr

Reputation: 171226

The SOLID principles are ideas/guidelines to help you come up with better solutions. There is nothing inherently gained from following these principles.

The inheritance strategy posted here does not work well. It accomplishes nothing and causes confusion and more work. This is not a good way to go.

But it could violate Single Responsibility Principle (SRP) and lead to very large SuperClass.

The SRP is very vague in terms of what a "single responsibility" is. You can define that as narrow or as wide as you want. Again, the principle is just there to guide you and make you think about not needlessly mixing things that should be separate.

Here, you can say "The responsibility of this class is to be the model for data binding the view."

SuperClass

The same. It's just a guideline. You can never say "a class can only have N members at most". This advice is wrong for any N because N is context sensitive.

Upvotes: 0

Jan Linxweiler
Jan Linxweiler

Reputation: 392

In order to make OCP work for you you should identify the behaviour that your client(s)need(s). This knowledge can be used build up one ore more interfaces that your ViewModel classes then implement. To avoid your inheritance hierarchy you could then prefer composition to structure your ViewModels the way

Upvotes: 0

Related Questions