2

У меня есть следующий класс дизайн. Полный код доступен в «How to achieve this functionality using Generics?». Код прекрасно работает и решает проблему литья, упомянутую в «Refactoring Code to avoid Type Casting»Рефакторинг класс дизайна для передачи намерений дизайна

В RetailInvestmentReturnCalculator классе метод GetInvestmentProfit() использует CalculateBaseProfit() метод, присутствующий в InvestmentReturnCalculator абстрактного базового класса. Этот факт не проявляется в дизайне класса.

ВОПРОС

  1. Как реорганизовать этот дизайн класса передать вышеупомянутый факт?
  2. Что такое руководство по дизайну, которое предотвратит подобные ошибки дизайна?

Примечание: Martin Fowler: Is Design Dead? говорит

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

Class Diagram

enter image description here

Аннотация

public abstract class InvestmentReturnCalculator 
{ 
    #region Public 

    public double ProfitElement { get; set; } 
    public abstract double GetInvestmentProfit(); 

    #endregion 

    #region Protected 

    protected double CalculateBaseProfit() 
    { 
     double profit = 0; 
     if (ProfitElement < 5) 
     { 
      profit = ProfitElement * 5/100; 
     } 
     else 
     { 
      profit = ProfitElement * 10/100; 
     } 
     return profit; 
    } 

    #endregion 
} 

public abstract class InvestmentReturnCalculator<T> : InvestmentReturnCalculator where T : IBusiness 
{ 
    public T BusinessType { get; set; } 
} 

Бетон

public class RetailInvestmentReturnCalculator : InvestmentReturnCalculator<IRetailBusiness> 
{ 
    public RetailInvestmentReturnCalculator(IRetailBusiness retail) 
    { 
     BusinessType = retail; 
     //Business = new BookShop(100); 
    } 

    public override double GetInvestmentProfit() 
    { 
     ProfitElement = BusinessType.GrossRevenue; 
     return CalculateBaseProfit(); 
    } 
} 
+1

Как это не видно из дизайна класса? Создание модификатора доступа 'protected', а не' private', делает это очевидным. Что еще более важно, возможно, этот метод «GetInvestmentProfit» не имеет хорошего названия, поскольку он не описывает ДВА отдельных вещей, которые он делает ... Возможно, его нужно переименовать? как насчет 'SetProfitElementAndCalculateProfit()'? –

+0

@CharlesBretana Спасибо за 'частную идею. Разве это не 'ДВА отдельных вещей' в методе, который нужно реорганизовать? – Lijo

+3

Обычно методы, классы и т. Д. Должны делать ОДНУ вещь и делать это хорошо. Это называется принципом единой ответственности (SRP). И это очень хороший общий принцип. Однако, когда вы перемещаетесь вверх и вниз в стеке, то, что на самом деле означает «ОДИН ВЕЩЬ», может измениться по мере изменения уровня абстракции. На высоком уровне это может быть «Заказать книгу». Несколько слоев вниз, это может означать корректировку инвентаря, перенос книги на отправку, отправку счета и т. Д. Но на каждом уровне один метод должен отвечать за выполнение одной задачи на этом уровне абстракции ... –

ответ

2

Поле ProfitElement довольно уродливое. Я бы сделал его абстрактным свойством на InvestmentReturnCalculator и реализовал его в базовых классах (вместо установки значения) - это называется template method pattern. Тогда вам не нужен метод GetInvestmentProfit().

public abstract class InvestmentReturnCalculator 
{ 
    #region Public 

    public abstract double ProfitElement { get; } 

    #endregion 

    #region Protected 

    public double GetInvestmentProfit() 
    { 
     double profit = 0; 
     if (ProfitElement < 5) 
     { 
      profit = ProfitElement * 5/100; 
     } 
     else 
     { 
      profit = ProfitElement * 10/100; 
     } 
     return profit; 
    } 

    #endregion 
} 

public abstract class InvestmentReturnCalculator<T> : InvestmentReturnCalculator where T : IBusiness 
{ 
    public T BusinessType { get; set; } 
} 

public class RetailInvestmentReturnCalculator : InvestmentReturnCalculator<IRetailBusiness> 
{ 
    public RetailInvestmentReturnCalculator(IRetailBusiness retail) 
    { 
     BusinessType = retail; 
     //Business = new BookShop(100); 
    } 

    public override double ProfitElement {get { return BusinessType.GrossRevenue;}} 

} 
+0

Хорошее решение. Однако это шаблон шаблона 'Method'? Мы переопределяем «свойство» в производном классе (который в конечном итоге вызывается базовым классом); а не 'метод', не так ли? – Lijo

+0

Кроме того, для ProfitElement в производном классе отсутствует ключевое слово 'override'. И 'GetInvestmentProfit()' должен быть сделан 'public' – Lijo

+2

@Lijo, спасибо за комментарии, исправил ошибки.Что касается вашего первого комментария, мы переопределяем геттер, который является методом. Свойство - это всего лишь синтаксический сахар вокруг пары методов: getter + setter. – Grzenio