views:

249

answers:

5

OCP (Open/Closed Principle) is one of the SOLID principles. Which is says:

”Software Entities should be Open for Extension, but Closed for Modification.”

It take me while to understand the above sentence about OCP. And when I start read more about it, I found it make sense and so useful, but in the mean time I noticed it cause duplicated code.

How such an important principle "OCP" will be the reason of massive code duplication practice?

namespace SOLIDPrinciples
 {  
    public class ReportFormatter {
     public virtual void FormatReport() {
      Console.WriteLine("Formatting Report for 8-1/2 X 11 ....");
     }
    }

    public class TabloidReportFormatter : ReportFormatter {
     public override void FormatReport() {
      Console.WriteLine("Formatting Report for 11 X 17 ....");
     } 
    }
 }

Am I missing something here? Is there another way for OCP to be explained?

A: 

What you have looks perfectly fine to me. You didn't modify ReportFormatter, you extended it.

Vadim
"FormatReport" isn't been duplicated here??This is only one method, what about 10s or 100s of methods.Am I wrong? If yes, can i get more clearification here, please.
egyamado
I believe you are missing the point of the principle. Try this: go back to what you had before trying to apply the OCP. Then look at the code that uses your ReportFormatters. Let's call it "ReportCreator". Now imagine creating a new kind of formatter. If you have to modify "ReportCreator" to add a new ReportFormatter, you have violated the OCP. The OCP does not mean "use inheritance".
SingleShot
A: 

Something like this could be solved with better design of the methods. Consider the following instead:

namespace SOLIDPrincibles
 {  
    public class ReportFormatter {
        public virtual void FormatReport(String size) {
                Console.WriteLine("Formatting Report for " + size + " ....");
        }
    }

    public class TabloidReportFormatter : ReportFormatter {
        public override void FormatReport() {
                super.FormatReport("11 X 17");
        }       
    }
 }

I think design along these lines would be your best shot of eliminating duplicated code.

Cuga
I am assuming those Console outputs are placeholders for some real code that does his actual formatting, so what you suggest may not make sense. If the code really is to be used as-is, I would take what you did and roll it all the way up into a single class that has a "setter" for reportFormat rather than have any inheritance.
SingleShot
A: 

Having implemented a custom format report system myself, I'd say the approach is wrong already.

Why have a formatter class for each format when you give the format as an argument to the constructor? The formatter class ctor (or even the static format method of the ReportFormatter class) should have a report and a format description as arguments and format the report accordingly.

There is just no need for a class for each format.

haffax
A: 

Something like this should work.

namespace SOLIDPrinciples
 {  
    public class ReportFormatter {
        public virtual void FormatReport() = 0;
    }

    public class VariableSizeReportFormatter : ReportFormatter {
        public void SetSize(String size);
        public override void FormatReport() {
                Console.WriteLine("implement generic layout routines");
        }       
    }

    public class TabloidReportFormatter : VariableSizeReportFormatter {
        public override void FormatReport() {
                // currently, we just do a tweaked version of generic layout ..
                SetSize("11x7");
                Super.Format(); 
                Console.WriteLine("RemoveThatColumnWeDontWant");
        }       
    }
 }

That means:

  • clients of ReportFormatter don't need to change (assuming some mechanism elsewhere that handles setup).
  • an improvement to generic formatting capabilities can be added to improves all reports, so no duplicated code
  • you just need things to work better in some particular case, you can just make that change

It's the third point that is the key: you can say 'in principle, the smart layout routine should know to drop the year from the date if the comment makes the row too big to fit'. But implementing that change in practise can be a nightmare, if it means that then the other report comes out wrong, so you have to change the logic of how it all works then retest every report layout in the system...

soru
+1  A: 

Code duplication refers to the significant repeating of blocks, statements, or even groupings of common member declarations. It doesn't refer to the repeating of language keywords, identifiers, patterns, etc. You wouldn't be able to achieve polymorphism otherwise.

The example you provide doesn't really demonstrate the Open/Closed Principle because you haven't demonstrated how a given class's behavior can be extended without modification. The Open/Closed principle is about creating new classes each time variant behavior is desired. This can be achieved using inheritance along with the Template Method pattern (i.e. calling abstract or virtual methods in a base class which are overridden in subclasses to achieve the desired/new behavior), but it's more often demonstrated using composition using the Strategy pattern (i.e. encapsulating the variant behavior in class and passing it to the closed type to be used in determining the overall behavior achieved).

From your example, it appears you were thinking more along the lines of trying to achieving OCP through inheritance, but starting with a base report formatter to establish the interface with subtypes to indicate different types of formats for a given report is actually a good start toward showing OCP through composition. What's needed to demonstrate OCP with a composition-based approach is a class which consumes the formatters ... say a ReportWriter.

public class ReportWriter : IReportWriter
{
    Write(IReportData data, IReportFormatter reportFormatter)
    {
         // ...

         var formattedStream = reportFormatter.Format(data, stream);

        // ...
    }
}

Just for the sake of demonstration, I've made some assumptions about how our new formatter might behave, so don't focus too heavily on that part. The thing to focus on is that ReportWriter is open for extension by allowing new formatters to be passed in, thus affecting how reports are ultimately written, but closed for modification of code needed to achieve this new behavior (i.e. the presence of if statements, switch statements, etc. to achieve multiple ways to format).

Not only does the Open/Closed principle not cause code duplication, when achieved through the use of composition over inheritance it actually can help reduce duplication. If true code duplication occurs in the course of creating your inheritance hierarchy or strategy classes then that points to a factoring issue unrelated to the fact that it might exist in the context of you trying to achieve OCP.

Derek Greer
Wrong. Typical application of OCP involves creating a subclass with a different implementation for some overridable method, which is exactly what the example in the question does.
Rogerio
Actually, OCP is typically demonstrated using composition rather than inheritance. As I said, OCP can be achieved using inheritance using the Template Method pattern, but every instance of polymorphism isn't an example of OCP. All that is shown in the example above is basic virtual dispatch (i.e. method overriding). To demonstrate OCP, you have to show how the behavior can be reused (not replaced entirely) by using extension points like the Template Method Pattern, or better, the Strategy Pattern.
Derek Greer