Abstract methods and the principle of open closing

Suppose I have the following contrived code:

abstract class Root { public abstract void PrintHierarchy(); } class Level1 : Root { override public void PrintHierarchy() { Console.WriteLine("Level1 is a child of Root"); } } class Level2 : Level1 { override public void PrintHierarchy() { Console.WriteLine("Level2 is a child of Level1"); base.PrintHierarchy(); } } 

If I consider only the Level2 class, I immediately see that Level2.PrintHierarchy follows the open / closed principle , because it does something by itself, and it calls the base method, which it overrides.

However, if I consider only the Level1 class, it seems to violate OCP because it does not call base.PrintHierarchy - in fact, in C # the compiler forbids it with the error "Can't call" abstract base element ".

The only way to make Level1 seem to follow OCP is to change Root.PrintHierarchy to an empty virtual method, but then I can no longer rely on the compiler to force classes to implement PrintHierarchy .

The real problem I'm having while saving the code here is to see dozens of override methods that don't call base.Whatever() . If base.Whatever is abstract, then accurate, but if not, then the Whatever method may be a candidate that should be pulled into the interface, and not a specific method that can override, or the class or method needs to be reorganized into some other mode, but in any case, it clearly indicates a poor design.

Except that Root.PrintHierarchy is abstract or puts a comment inside Level1.PrintHierarchy , do I have any other options to quickly determine if a class like Level1 breaking OCP?


There was a lot of good discussion in the comments as well as good answers. I am having trouble figuring out exactly what you can ask. I think I'm upset by what @Jon Hanna points out , sometimes the virtual method simply indicates: “You have to implement me”, while in other cases it means “you have to forgive me - if you do not call the basic version, you will break my project!" But C # does not offer any way to indicate which one you have in mind, other than this abstract or interface, it is obviously a “mandatory implementation” of the situation. (Is it something in the Codes of Contracts, which, in my opinion, is a little beyond)

But if a binding language was installed in any language vs. a must-extend decorator, this would probably create huge problems for unit testing if it couldn’t be disabled. Are there any such languages? It sounds more like a design under a contract , so I wouldn’t be surprised if he were in Eiffel, for example.

The end result is probably, as @ JordĂŁo says , and it is completely contextual; but I'm going to leave the discussion open for a while before accepting any answers.

+7
c # oop open-closed-principle
source share
3 answers

Root defines the following: Root objects have a PrintHierarchy method. He says nothing more about the PrintHierarchy method.

Level1 has a PrintHierarchy method. He does not stop using the PrintHierarchy method, so he in no way violates the principle of open / closed mode.

Now, in more detail: rename your "PrintHierarchy" to "Foo". Does Level 2 mean or violate the principle of opening / closing?

The answer is that we don’t have a clue because we don’t know what the semantics of “Foo” are. Therefore, we do not know whether to refer to base.Foo after the rest of the body of the method, before the rest, in the middle or not at all.

If 1.ToString() returns "System.ObjectSystem.ValueType1" or "1System.ValueTypeSystem.Object" to preserve this pretense when opening / closing, or assign the base.ToString() call to an unused variable before returning "1"?

Obviously none of them. It should return as meaningful a string as possible. Its base types return as meaningful a string as they can, and an extension that does not have the advantage of being called to its base.

The open / closed principle means that when I call Foo (), I expect some kind of Fooing to happen, and I expect that the corresponding Fooing Level1 will match when I call it on Level1 and Some Level2, suitable for Fooing, when I will call him on Level2. Regardless of whether Level2 Fooing will include some Level1 Fooing, it depends on what Fooing is.

base is a tool that helps us extend classes, not requirements.

+5
source share

You cannot decide whether the system (or class) should follow OCP by simply looking at it statically, without contextual information.

Only if you know what the likely design changes are, can you judge whether this follows the OCP for those specific kinds of changes.

There is no language construct that can help you with this.

A good heuristic would be to use some kind of metric, for example, Robert Martin instability and abstractness (pdf) or metrics due to lack of cohesion in order to better prepare your systems for changes and have a better chance of following OCP and all other important OOD principles.

+3
source share

OCP is all the prerequisites and postconditions: "when you can replace your precondition with a weaker one and its postcondition with a stronger one." I do not think that calling the database in overridden methods violates it.

+1
source share

All Articles