Применение принципа единой ответственности в классе?

Принцип единой ответственности (SRP): -

Каждый класс должен нести единственную ответственность. По сути, для изменений должна быть одна причина. Я не уверен, что именно означает последнее утверждение. Моя интерпретация состоит в том, что для проектирования классов должна быть единственная причина для изменения - может быть единственный метод, потому что каждый метод - это поведение и, следовательно, причина. Это верно? Если нет, то что именно определяет причину?

Рассмотрим систему фондовой биржи, в которой большинство разработчиков придумывают дизайн, в котором StockService.java имеет методы покупки и продажи. Здесь будет две причины (купить и продать) изменить этот класс.

public class StockService {
    
   private String name = "ABC";
   private int quantity = 10;

   public void buy(){
      System.out.println("Stock [ Name: "+name+", 
         Quantity: " + quantity +" ] bought");
   }
   public void sell(){
      System.out.println("Stock [ Name: "+name+", 
         Quantity: " + quantity +" ] sold");
   }
   
   // other methods related to socks
}

Чтобы следовать принципу SRM, мне нужно создать отдельный класс, где StockBuyService.java (содержащий методы, связанные с покупкой) и StockSellService (содержащий методы, связанные с продажей). это ?


person emilly    schedule 24.09.2017    source источник
comment
Количество методов не имеет значения. См. принцип единой ответственности, применимый к функциям на программная инженерия SE. Это связано с участниками или ролями в вариантах использования, а не с количеством методов.   -  person MatsLindh    schedule 24.09.2017


Ответы (2)


Не обязательно. Методы buy() и sell() имеют смысл вместе, если учесть, что цель класса - StockService. Судя по названию, можно предположить, что биржевой сервис будет обрабатывать торговые запросы от инвестора, и эти торговые запросы могут быть либо buy(), либо sell(). (Фактически, стандартная служба с единственным методом trade() может рисковать нарушить принцип разделения интерфейса, в зависимости от того, как он реализован.)

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

Но поскольку вы задаете вопрос, это показатель того, что название может быть недостаточно ясным. (На практике рефакторинга это известно как запах кода.) Это хорошее время, чтобы просмотреть его и спросить себя, правильно ли назван класс. Это действительно стоковая услуга? Были бы вы столь же неуверенны, если бы класс назывался StockTradingService? Можете ли вы придумать лучшее имя, которое более четко выражает цель, которой служит этот класс? Я не говорю, что это плохое имя или что его нужно изменить; Я только говорю, что вы должны осознать, что необходимость задать вопрос должна вызвать у вас некоторую «осторожность».

person John Deters    schedule 26.09.2017

Во-первых, как прокомментировал @MatsLindh, количество методов не является фактором при оценке SRP.

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

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

public class StockService{
  PriceService priceService;
  AccountService accountService;
  Stock stock

  public void buy(int amount){
    int fullPrice = getFullPrice(amount);
    if(accountService.hasEnoughMoney(fullPrice){
         accountService.buyStock(stock, amount, fullPrice)
    }
  }
  public void sell(int amount){
     accountService.sellStock(stock,amount)
  }
  private int getFullPrice(int amount){
     return amount * priceService.getPrice(stock);
  }
}

Как видите, у класса есть только одна обязанность: управлять сделкой. Он ничего не знает о деталях (как реализовать проверку или покупку). Единственная причина изменить класс - это изменение торгового потока.

На мой взгляд, SRP - это поиск надлежащего уровня абстракции для определения цели и реализации как можно меньшего количества кода или логики, чтобы охватить эту цель и позволить другим классам сделать все остальное.

person Zsolt V    schedule 28.09.2017