В ООП обязательно ли иметь больше классов с меньшим количеством методов, чем меньше классов с большим количеством методов? - PullRequest
1 голос
/ 20 марта 2019

Я изучал шаблоны проектирования и наткнулся на Шаблон посетителя. Это действительно странная вещь для меня; кажется, что его единственная цель состоит в том, чтобы предотвратить необходимость изменения исходных классов в иерархии, даже если к ним добавлена ​​новая ответственность. Это происходит просто путем делегирования этих обязанностей реализации абстрактного класса (или интерфейса) посетителя и эффективной двойной диспетчеризации для вызова рабочей функции правильного посетителя, передающего правильный динамический тип.

Я изучал различные другие вопросы, обсуждающие, когда и зачем использовать шаблон посетителя, и все, с чем я столкнулся, это то, что в ООП ожидается, что число ваших классов будет расти, а не количество методов в классе. , на котором специализируется Шаблон посетителя.

Однако это правда? Это звучит как грубое упрощение ООП для меня. Для справки, этот ответ пришел от здесь

Я также прочитал еще один (слабо) связанный вопрос здесь, , ответы на которые подразумевают, что вы должны создавать больше классов и разделять обязанности только там, где это имеет смысл. Действительно ли имеет смысл организовать ваш код таким образом, чтобы «ответственность» класса определялась просто одной операцией, независимо от того, над каким типом он работает (т. Е. Посетитель отвечает за реализацию операции для каждого соответствующего типа) Или же гораздо разумнее определить ответственность как набор операций, которые могут быть выполнены для одного типа (делегировать эти обязанности самим типам)?

Если цель состоит в том, чтобы просто сократить количество строк кода в каждом классе, почему это законная цель? Количество строк само по себе не имеет прямого отношения к инкапсуляции, модульности, организации кода или общим хорошим методам ООП, верно?

Edit: Теперь я понимаю, что шаблоны не существуют для обслуживания ООП, и теперь я понимаю, что в некоторых случаях шаблон Visitor является лучшим решением. При этом, способствует ли это или не способствует менее разумной иерархии классов при использовании вместе с ООП?

Ответы [ 3 ]

1 голос
/ 20 марта 2019

Я думаю, вы, возможно, неправильно поняли, что делают Шаблон посетителя и Шаблоны в целом, и почему мы иногда их используем.

Это не из-за некоторой ООП-вещи, некоторого абстрактногоКонцепция или какая-то скрытая правда.Это совсем наоборот.От вас ожидают, что вы смоделируете свою проблему должным образом объектно-ориентированным способом, используя объекты со поведением , скрытым состоянием и все такое.Это является OO в двух словах.Вы не должны выходить на улицу и искать шаблоны для случайного применения в вашем коде, фактически это само по себе является анти-шаблоном:)

Теперь некоторые люди заметили, чтоЕсть определенные небольшие проблемы, с которыми большинство из нас стремятся найти аналогичные решения.Они интересны не потому, что решения настолько новаторские или ценные сами по себе, а потому, что это просто экономит нам время и силы, чтобы не решать их снова и снова.Это паттерны.

Возвращаясь к вашему вопросу: Если вы не совсем понимаете, что делает паттерн посетителя, или почему это делает, или как / почемуможно было бы использовать его, не беспокойтесь об этом.Если вы столкнетесь с проблемой, которая нужна , вы заметите!Это ситуация, когда больше ничего не будет работать, и вам, по сути, придется использовать .Во всех других ситуациях вы не должны использовать его!

Резюме: Нет, мы не используем шаблон посетителя из-за какого-то ООП.Сокращение линий само по себе не является законной целью.Да, только количество строк не влияет на инкапсуляцию, модульность и т. Д.

0 голосов
/ 20 марта 2019

Объектно-ориентированное программное обеспечение наиболее пригодно для поддержки в долгосрочной перспективе, если оно разработано с учетом SOLID объектно-ориентированных принципов . Шаблоны проектирования - это только что названные, многократно используемые приложения этих принципов.

Цель шаблона посетителя - НЕ предотвращать необходимость изменения исходных классов в иерархии «даже когда к ним добавляется новая ответственность». Скорее, это значит, что эти классы должны быть расширены до без изменения , что является прекрасным примером принципа открытия / закрытия.

Но, чтобы ответить на ваши конкретные вопросы:

  1. Правильно. Сокращение количества строк не является целью. На самом деле, я могу показать вам класс, где использование принципа единой ответственности увеличит количество строк кода. (См. Ниже.)

  2. Ничего "рядом с ООП" в шаблоне посетителя нет. И нет. Это не продвигает менее разумную иерархию классов. Он позволяет добавлять поведение в класс без изменения этого класса, что в конечном итоге делает ваше программное обеспечение более обслуживаемым.

Вот пример, который я обещал (в Ruby с аннотациями в конце):

class OrderTotalCaluculator
  attr_reader :order, :tax_calculators

  def initialize(order, tax_calculators = [])
    @order = order
    @tax_calculators = tax_calculators
  end

  def total
    items_subtotal = order.items.sum { |item| item.price * item.quantity }
    taxes_subtotal = tax_calculators.sum { |calculator| calculator.calculate(items_subtotal) }

    items_subtotal + taxes_subtotal
  end
end

class CaliforniaStateSalesTaxCalculator
  def self.calculate(amount)
    amount * 0.075
  end
end

class SanFranciscoCitySalesTaxCalculator
  def self.calculate(amount)
    amount * 0.01
  end
end

total = OrderTotalCalculator.new(
  order, 
  [
    CaliforniaStateSalesTaxCalculator, 
    SanFranciscoCitySalesTaxCalculator
  ]
).total

В целом, этот код - который реализует шаблон Стратегии - довольно хорош. Каждый класс несет одну ответственность. Кроме того, если бы США когда-либо создавали национальный налог с продаж, OrderTotalCalculator не нужно было бы менять.

Но, если мы посмотрим поближе, метод OrderTotalCalculator#total несет более чем одну ответственность. Вот как бы это выглядело, если бы мы это убрали:

class OrderTotalCaluculator
  attr_reader :order, :tax_calculators

  def initialize(order, tax_calculators = [])
    @order = order
    @tax_calculators = tax_calculators
  end

  def total
    items_subtotal + taxes_subtotal
  end

  def items_subtotal
    @items_subtotal ||= order.items.sum { |item| item.price * item.quantity }
  end

  def taxes_subtotal
    tax_calculators.sum { |calculator| calculator.calculate(items_subtotal) }
  end
end

Теперь буквально каждый метод имеет одну, четко определенную, хорошо названную ответственность. И класс действительно вырос в размерах. Итак, поехали.

Рубин Аннотации:

  • @variable создает переменную экземпляра с именем variable
  • attr_reader :variable синтаксический сахар для создания метода получения для переменной экземпляра @variable
  • @variable ||= statement запоминает значение оператора в переменной экземпляра, чтобы при следующем вызове метода оператор не выполнялся снова
0 голосов
/ 20 марта 2019

Я начну с того, что скажу, что я не эксперт по шаблонам или догматам ООП.Я просто разработчик, который ежедневно использует шаблоны и ООП.

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

...