1

I'm having trouble getting the idea behind open closed principle.

As far as I know, the principle says that every class should be closed for modification and open for extension. right?

So it means that we have to make every method in our class virtual so we can extend the class without needing to modify it. Is this correct?

jaco0646
  • 15,303
  • 7
  • 59
  • 83
PNarimani
  • 614
  • 7
  • 23
  • 1
    "should be" != "must be". It means, if you want that method to be an extension point - the extension should happen without modifying base class. If a method is not intended to be an extension point - it should not be virtual. Even further - if a class is not designed to be an extension point - it should be `final` (by default). – zerkms Apr 01 '19 at 23:16
  • @zerkms Imagine that you have a plugin that you don't know how your users going to use/extend it. Is it right to make every method virtual except for the ones that you know overriding them might break functionality? – PNarimani Apr 01 '19 at 23:25
  • 1
    "you don't know how your users going to use/extend it" --- you cannot design for unknown: every way of something to be extended should be known and documented. If you designed for all methods to be extensible and your code supports it - indeed make them virtual. "except for the ones that you know overriding them might break functionality?" --- I'd say the opposite. – zerkms Apr 01 '19 at 23:37

1 Answers1

0

I think you are somewhat misunderstanding what the Open-Closed principle entails.

The open-closed principle states that software entities should be closed for modification and open to extension. Software entities in this context refers to classes, structs methods etc.

The basic idea is that your entities should not need to change often to requirements. If new requirements come along, your entities should be extended to accommodate the new functionality.

Suppose we have the following function:

    public List<Product> GetProducts(Criteria criteria)
    {
        return context.Where(x => x.criteria1 = criteria.criteria1)
            .Where(x => x.criteria2 = criteria.criteria2)
            .Where(x => x.criteria3 = criteria.criteria3)
            .Where(x => x.criteria4 = criteria.criteria4);
    }

It is easy to see that when another criteria requirement comes along, this will require modification to the "ProductRepository" class and it's functions. Ofcourse, many more functions in this class might need to be modified as it may also use criteria objects.

Suppose we can write something like this instead:

    private List<Product> GetProducts(Criteria criteria)
    {
        return cleverContext.Apply(criteria);
    }

We immediately gain the flexibility where only the code that is responsible for the criteria is modified or extended.

The biggest ways of improving design to satisfy the Open-Closed principle is to use a rich domain. When your domain models enterprise business rules, the rules should never change. We separate Domain with Use-Cases to deal with application rules.

Uncle bob writes more on this in his clean architecture.

Let me know if there is anything that I can shed light on, or try to explain further :)

Marcel De Villiers
  • 1,682
  • 4
  • 15
  • 17
  • So if I understand it correctly, if you apply OCP correctly, when a new requirement comes in, you only need to modify the part of code that corresponds with that requirement. And as a result modifications of other classes are minimized. Right? – PNarimani May 03 '19 at 20:03
  • @MoNarimani That is correct. From an architecture perspective, your domain should change if the enterprise rules change. Use cases should change when your application rules change. The key concept to understand is that extension does not necessarily mean inheritance. It can refer to composition, and more often does. When your objects are easily reused, they define a single goal and has only one business reason to change. This ensures other classes needs minimal modification. – Marcel De Villiers May 09 '19 at 11:53