Насколько такой важный принцип «OCP» станет причиной массовой практики дублирования кода? - PullRequest
0 голосов
/ 12 сентября 2009

OCP (открытый / закрытый принцип) является одним из принципов SOLID. Что говорит:

”Программные объекты должны быть открыты для расширения, но закрыты для модификации.”

Мне понадобилось время, чтобы понять вышеприведенное предложение об OCP. И когда я начал читать больше об этом, я обнаружил, что это имеет смысл и настолько полезен, но в то же время я заметил, что это приводит к дублированию кода.

Как такой важный принцип «OCP» станет причиной массовой практики дублирования кода?

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 ....");
        }   
    }
 }

Я что-то здесь упускаю? Есть ли другой способ объяснить OCP?

Ответы [ 5 ]

4 голосов
/ 20 октября 2009

Дублирование кода относится к значительному повторению блоков, операторов или даже групп общих объявлений членов. Это не относится к повторению ключевых слов, идентификаторов, шаблонов и т. Д. В противном случае вы не сможете достичь полиморфизма.

Пример, который вы предоставляете, на самом деле не демонстрирует принцип Open / Closed, потому что вы не продемонстрировали, как поведение данного класса может быть расширено без модификации. Принцип Open / Closed заключается в создании новых классов каждый раз, когда требуется вариант поведения. Это может быть достигнуто с помощью наследования вместе с шаблоном Template Method (т. Е. Путем вызова абстрактных или виртуальных методов в базовом классе, которые переопределяются в подклассах для достижения желаемого / нового поведения), но чаще это демонстрируется с использованием композиции с использованием шаблона Strategy (т.е. инкапсуляция варианта поведения в классе и передача его закрытому типу для использования при определении общего достигнутого поведения).

Из вашего примера видно, что вы больше размышляли о попытках достижения OCP с помощью наследования, но, начиная с базового средства форматирования отчетов, чтобы установить интерфейс с подтипами для указания различных типов форматов для данного отчета, на самом деле хорошее начало, чтобы показать OCP через композицию. Для демонстрации OCP с композиционным подходом необходим класс, который использует средства форматирования ... скажем, ReportWriter.

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

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

        // ...
    }
}

Просто для демонстрации я сделал несколько предположений о том, как может вести себя наш новый форматер, поэтому не зацикливайтесь на этой части. Особое внимание следует уделить тому, что ReportWriter открыт для расширения, позволяя передавать новые средства форматирования, что влияет на то, как отчеты в конечном итоге пишутся, но закрывается для изменения кода, необходимого для достижения этого нового поведения (то есть наличия операторов if, switch заявления и т. д. для достижения нескольких способов форматирования).

Мало того, что принцип Open / Closed не только не вызывает дублирование кода, но достигается за счет использования композиции вместо наследования, но на самом деле может помочь уменьшить дублирование. Если в ходе создания иерархии наследования или классов стратегии происходит истинное дублирование кода, это указывает на проблему факторинга, не связанную с тем фактом, что она может существовать в контексте вашей попытки достижения OCP.

0 голосов
/ 12 сентября 2009

Как-то так должно работать.

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");
        }       
    }
 }

Это значит:

  • клиенты ReportFormatter не нуждаются в изменении (при условии, что в другом месте есть механизм, выполняющий настройку).
  • может быть добавлено улучшение общих возможностей форматирования для улучшения всех отчетов, поэтому дублирующийся код отсутствует
  • вам просто нужны вещи, чтобы работать лучше в каком-то конкретном случае, вы можете просто внести это изменение

Это третий пункт, который является ключевым: вы можете сказать «в принципе, подпрограмма интеллектуального макета должна знать, что нужно отбрасывать год с даты, если комментарий делает строку слишком большой, чтобы уместиться». Но реализовать это изменение на практике может быть кошмаром, если это означает, что другой отчет выходит неверным, поэтому вам нужно изменить логику того, как все это работает, а затем повторно протестировать каждый макет отчета в системе ...

0 голосов
/ 12 сентября 2009

Нечто подобное можно решить с помощью лучшего дизайна методов. Вместо этого рассмотрим следующее:

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");
        }       
    }
 }

Я думаю, что проектирование в этом направлении было бы вашим лучшим способом устранения дублированного кода.

0 голосов
/ 12 сентября 2009

Внедрив систему отчетов в произвольном формате, я бы сказал, что этот подход уже неправильный.

Зачем нужен класс форматера для каждого формата, когда вы предоставляете формат в качестве аргумента конструктору? Класс ctor форматера (или даже метод статического формата класса ReportFormatter) должен иметь отчет и описание формата в качестве аргументов и соответственно форматировать отчет.

Нет необходимости в классе для каждого формата.

0 голосов
/ 12 сентября 2009

То, что у тебя есть, выглядит прекрасно для меня. Вы не изменили ReportFormatter, вы расширили его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...