лучший шаблон проектирования для рефакторинга набора классов, которые выполняют базу вычислений по многим параметрам

Я рефакторинг набора классов, который делает некоторые расчеты цены. расчет выполняется на основе многих параметров. Код:

public interface IParcel {
    int SourceCode { get; set; }
    int DestinationCode { get; set; }
    int weight{get;set;}
    decimal CalculatePrice();
}

public abstract class GeneralParcel : IParcel {
    //implementation of inteface properties
    //these properties set with in SourceCode & DestinationCode 
    //and are used in CalculatePrice() inside classes that inherit from GeneralParcel 
    protected SourceProvinceCode{get; protected set;}
    protected DestinationProvinceCode{get;protected set;}

    //private variables we need for calculations
    private static ReadOnlyDictionary<int, List<int>> _States_neighboureness;
    private static ReadOnlyCollection<City> _Citieslist;
    private static ReadOnlyCollection<Province> _Provinceslist;

    protected ReadOnlyCollection<City> Citieslist {get { return _Citieslist; }}

    protected ReadOnlyCollection<Province> ProvincesList {get { return _Provinceslist; }}

    protected ReadOnlyDictionary<int, List<int>> StatesNeighboureness {get {return _States_neighboureness; }}

    //constructor code that initializes the static variables

    //implementation is in concrete classes
    public abstract decimal CalculatePrice();
}

public ExpressParcel : GeneralParcel {
    public decimal CalculatePrice() {
        //use of those three static variables in calculations
        // plus other properties & parameters
        // for calculating prices 
    }
}


public SpecialParcel : GeneralParcel {
    public decimal CalculatePrice() {
        //use of those three static variables in calculations
        // plus other properties & parameters
        // for calculating prices 
    }
}

В настоящий момент код эффективно использует «Шаблон стратегии» .

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

Имеет ли еще один интерфейс, как это необходимо ниже (& затем переносить эти статические свойства внутри него ?, даже сделать статический этот класс, потому что это в основном только некоторые вычисления), то как подключить его к IParcel? Поступая таким образом, как наилучшим образом реализовать CalculatePrice () в SpecialParcel & классы ExpressParcel ?

public interface IPriceCalculator {
    decimal CalculatePrice();
}
8 голосов | спросил ahmad molaie 5 +04002011-10-05T23:18:26+04:00312011bEurope/MoscowWed, 05 Oct 2011 23:18:26 +0400 2011, 23:18:26

1 ответ


13

В зависимости от остальной части вашего дизайна наличие Parcel знает, как вычислить его собственное право, является вполне приемлемым поведением. Тем не менее, метод расчета теперь привязан непосредственно к самому классу парцелл (что произойдет, если ваше местное правительство решит, что ExpressParcels под конкретным вес должен быть заряжен как GeneralParcel - или, что еще хуже, какой-то другой подкласс?).

Кроме того, три словаря, скорее всего, not вообще принадлежат классу parcel. Я не могу себе представить, что это единственное место, которое относится к типу информации. Процедуры доставки /грузовика также приходят на ум. Вы должны убедиться, что это так, или если логика /данные действительно уникальны.

С учетом сказанного, здесь есть некоторые возможности для рефакторинга в этой ситуации. Вы на правильном пути с помощью IPriceCalculator; вы должны изменить его, чтобы взять объект IParcel.

1) Внедрить IPriceCalculator, сохранить как класс (static) в подклассах IParcel.

public class GeneralParcel : IParcel {

    private static IPriceCalculator calc = new IPriceCalculator() {

                                               public decimal Calculate(IParcel parcel) {
                                                  return weight * .1;
                                               }
                                           }
}

Это позволяет делиться ценовыми калькуляторами между классами, просматривать статический завод и т. д. Однако это не позволяет легко изменить калькулятор, используемый для определенного экземпляра этого класса (например, применяя купон правила). И вы все равно должны спросить посылку, насколько это важно.

2) Внедрить IPriceCalculator, сохранить как переменную экземпляра в IParcel.

public class GeneralParcel : IParcel {

    private IPriceCalculator calc;

    public decimal Price {
       get {
          return calc.Calculate(this);
       }
    }

    public GeneralParcel(IPriceCalculator calc) {
       this.calc = calc;
    }

}

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

3) Внедрить IPriceCalculator, искать из сохраненного кеша, сохранить результат в IParcel.

public class GeneralParcel : IParcel {

    private decimal price = -1;

    public decimal Price {
       get {
          if (price < 0) {
              price = CalculatorLookup.find(this).calculate(this);
          }
          return price;
        }
    }

    public GeneralParcel() {

    }
}

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


Наконец, мой любимый и самый гибкий;

4) Создайте (и реализуйте) серию * PriceCalculation * s, сохраните IPriceCalculation в ---- +: = 17 =: + ----.

IParcel

осущ

public interface IPriceCalculation {
   public decimal Calculate {get;}

   public IPriceCalculation AddRule(IPriceCalculation calc, IParcel parcel);
}

Я рекомендую, чтобы вычисления были структурными и неизменяемыми. Вероятно, вам захочется добавить к ним другие вещи, такие как способность сообщать о своей индивидуальной цене (для позиций), а не только итог. Кроме того, хотя immutablility поможет избежать циклических ссылок, вы можете добавить что-то для проверки существующих вычислений (вероятно, не должно быть двух public struct BaseFeeCalculation { private readonly decimal price; public decimal Calculate { get { return price; } } private BaseFeeCalculation (IPriceCalculation calc) { if (calc == null) { price = 5; } else { price = 5 + calc.Calculate; } } public IPriceCalculation AddRule(IPriceCalculation calc, IParcel parcel) { return new BaseFeeCalculation(calc); } } s, в конце концов).

Посылка в основном остается прежней; просто добавьте свойство, которое возвращает BaseFee.

При добавлении правил попробуйте что-нибудь в следующих строках:

IPriceCalculation

Это хорошо работает для чего-либо, кроме зависимых правил (хотя для этого достаточно легко адаптироваться). Обычно список вычислений может /должен быть инициализирован через фреймворк DI /Autowiring (например, Spring).

О, спасибо за использование public struct CalculationBuilder { // You'll need to initialize this somehow, obviously. private static readonly IList<IPriceCalculation> calculations; private CalculationBuilder() {} private static IPriceCalculation Build(IParcel parcel) { IPriceCalculation calc; foreach(IPriceCalculation calcer : calculations) { calc = calc.AddRule(calcer, parcel); } return calc; } } , а не decimal для расчета цены.

... это было немного долго, не так ли.

ответил Clockwork-Muse 6 +04002011-10-06T02:21:08+04:00312011bEurope/MoscowThu, 06 Oct 2011 02:21:08 +0400 2011, 02:21:08

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132