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


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

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


2609
8
задан 5 октября 2011 в 07:10 Источник Поделиться
Комментарии
1 ответ

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

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

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

1) реализовать IPriceCalculator, магазин как класса (статические) переменные в IParcel подклассы.

public class GeneralParcel : IParcel {

private static IPriceCalculator calc = new IPriceCalculator() {

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

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

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() {

}
}

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



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

4) создать (и реализовать) серия *PriceCalculation*ы, магазин IPriceCalculation в IParcel подкласс экземпляров.

public interface IPriceCalculation {
public decimal Calculate {get;}

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

осущ

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

}

Я рекомендую, что расчеты будут структуры, и неизменяемые. Вы, вероятно, будете хотеть, чтобы добавить другие вещи, как возможность представить свою индивидуальную цену (по позиции), а не просто общий. Кроме того, в то время как immutablility поможет избежать циклических ссылок, вы, возможно, захотите добавить что-то, чтобы проверить существующие вычисления (вероятно, не стоило два BaseFeeС, в конце концов).

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

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

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

Это хорошо работает, ни на что не хватает зависимые правила (хотя может быть достаточно легко приспособлены для этого). Как правило, список расчетов могут/должны быть инициализированы с помощью системы Ди/Автоэлектропроводка (как весной).

Да, и спасибо за использование десятичной, а не двойной для расчета цен.

... это было немного долго, правда.

13
ответ дан 5 октября 2011 в 10:10 Источник Поделиться