3

以前に助けを求めようとした問題がありますが、その時は解決できませんでした。そのため、問題を単純化して、これについてより具体的な助けが得られるかどうかを確認しようとしています。私を夢中にさせている…</p>

基本的に、私はこのアプリケーションの実用的な (より複雑な) バージョンを持っています。これはプロジェクト コスト計算ツールです。しかし、私は同時にアプリケーションをより良く設計する方法を学ぼうとしているので、この設計をどのように改善できるかについて意見を求めています. 基本的に私が欲しいのは、(ここでは) 2 つの場所で繰り返される条件文への入力です。以前に得た提案は、戦略パターンまたは工場パターンを使用することでした。また、マーティン・ファウラーの本で、ポリモーフィズムによる条件付きリファクタリングを提案していることも知っています。私は彼のより単純な例でその原則を理解しています。しかし、ここでこれらのことのいずれかを行うにはどうすればよいですか (適切な場合)。私の見方では、計算はいくつかの条件に依存しています: 1. それはどのような種類のサービスですか? 2. プロジェクトは小規模ですか。中か大か?(「製品は新しいものですか、それとも既存のものですか?」など、他のパラメーターも同様に異なる場合があることに注意してください。したがって、そのようなパラメーターは追加できるはずですが、例を単純にするために2つのパラメーターのみを使用するようにしました具体的な助けを得ることができます)

したがって、ポリモーフィズムを使用したリファクタリングは、最初の条件 (サービスの種類) に対して既に持っているいくつかのサブクラスを作成することを意味し、2 番目の条件 (サイズ) に対してもさらにサブクラスを作成する必要がありますか? AnalysisSmall、AnalysisMedium、AnalysisLarge、WritingSmallなど…??? いいえ、私はそれが良くないことを知っています.とにかくそのパターンを扱う方法がわかりませんか?

基本的に、戦略パターンを使用するという提案についても同じ問題が見られます (工場パターンは、上記のポリモーフィズムを達成するためのヘルパーにすぎません)。ですから、これらのクラスを最適な方法で設計する方法について具体的な提案があれば、本当に感謝しています! オブジェクトを正しく選択したかどうか、または再設計する必要があるかどうかも検討してください。(「工場のパターンを考慮する必要があります」などの回答は明らかに役に立ちません...私はすでにその道を進んでおり、この場合の正確な方法に困惑しています)

よろしく、

アンダース

コード (非常に単純化されています。データなどに構成ファイルを使用せずに、列挙型の代わりに文字列を使用しているという事実を気にしないでください。これらの設計上の問題を理解したら、実際のアプリケーションで必要に応じて実行されます。 ):

public abstract class Service
{
    protected Dictionary<string, int> _hours;
    protected const int SMALL = 2;
    protected const int MEDIUM = 8;

    public int NumberOfProducts { get; set; }
    public abstract int GetHours();
}

public class Writing : Service
{
    public Writing(int numberOfProducts)
    {
        NumberOfProducts = numberOfProducts;
        _hours = new Dictionary<string, int> { { "small", 125 }, { "medium", 100 }, { "large", 60 } };
    }

    public override int GetHours()
    {
        if (NumberOfProducts <= SMALL)
            return _hours["small"] * NumberOfProducts;
        if (NumberOfProducts <= MEDIUM)
            return (_hours["small"] * SMALL) + (_hours["medium"] * (NumberOfProducts - SMALL));
        return (_hours["small"] * SMALL) + (_hours["medium"] * (MEDIUM - SMALL))
            + (_hours["large"] * (NumberOfProducts - MEDIUM));
    }
}

public class Analysis : Service
{
    public Analysis(int numberOfProducts)
    {
        NumberOfProducts = numberOfProducts;
        _hours = new Dictionary<string, int> { { "small", 56 }, { "medium", 104 }, { "large", 200 } };
    }

    public override int GetHours()
    {
        if (NumberOfProducts <= SMALL)
            return _hours["small"];
        if (NumberOfProducts <= MEDIUM)
            return _hours["medium"];
        return _hours["large"];
    }
}

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        List<int> quantities = new List<int>();

        for (int i = 0; i < 100; i++)
        {
            quantities.Add(i);
        }
        comboBoxNumberOfProducts.DataSource = quantities;
    }

    private void comboBoxNumberOfProducts_SelectedIndexChanged(object sender, EventArgs e)
    {
        Service writing = new Writing((int) comboBoxNumberOfProducts.SelectedItem);
        Service analysis = new Analysis((int) comboBoxNumberOfProducts.SelectedItem);

        labelWriterHours.Text = writing.GetHours().ToString();
        labelAnalysisHours.Text = analysis.GetHours().ToString();
    }
}
4

3 に答える 3

2

あなたの計算では、サービスの種類、サービスのサイズ、製品の数の間には密接な関係があり、戦略パターンを適用するためにそれらをモジュラーチャンクに分離することは非常に困難です。

計算システムが固定されている場合、戦略パターンは適切ではないようです。そうでない場合は...まあ、システムを単純化してみませんか?

たとえば、サービスサイズから基本時間数を引き出し、他の設定に応じてさまざまな割引または増加を適用します。

public class Service
{
    public IServiceSize serviceSize { internal get; set; }
    public IServiceBulkRate serviceBulkRate { internal get; set; }
    public IServiceType serviceType { internal get; set; }
    public int numberOfProducts { get; set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="Service"/> class with default values
    /// </summary>
    public Service()
    {
        serviceSize = new SmallSize();
        serviceBulkRate = new FlatBulkRate();
        serviceType = new WritingService();
        numberOfProducts = 1;
    }

    public decimal CalculateHours()
    {
        decimal hours = serviceSize.GetBaseHours();
        hours = hours * serviceBulkRate.GetMultiplier(numberOfProducts);
        hours = hours * serviceType.GetMultiplier();

        return hours;
    }
}

public interface IServiceSize
{
    int GetBaseHours();
}

public class SmallSize : IServiceSize
{
    public int GetBaseHours()
    {
        return 125;
    }
}

public interface IServiceBulkRate
{
    decimal GetMultiplier(int numberOfProducts);
}

public class FlatBulkRate : IServiceBulkRate
{
    public decimal GetMultiplier(int numberOfProducts)
    {
        return numberOfProducts;
    }
}

public class StaggeredBulkRate : IServiceBulkRate
{
    public decimal GetMultiplier(int numberOfProducts)
    {
        if (numberOfProducts < 2)
            return numberOfProducts;
        else if (numberOfProducts >= 2 & numberOfProducts < 8)
            return numberOfProducts * 0.85m;
        else
            return numberOfProducts * 0.8m;
    }
}

public interface IServiceType
{
    decimal GetMultiplier();
}

public class WritingService : IServiceType
{
    public decimal GetMultiplier()
    {
        return 1.15m;
    }
}
于 2010-05-05T16:04:16.300 に答える
1

計算する値を選択するためのロジックを Service 基本クラスに移動し、実際の計算を各サブクラスに委譲します。

public abstract class Service
{
    private readonly int numberOfProducts;
    private readonly IDictionary<string, int> hours;
    protected const int SMALL = 2; 
    protected const int MEDIUM = 8;

    public Service(int numberOfProducts, IDictionary<string, int> hours)
    {
        this.numberOfProducts = numberOfProducts;
        this.hours = hours;
    }

    public int GetHours()
    {
        if(this.numberOfProducts <= SMALL)
            return this.CalculateSmallHours(this.hours["small"], this.numberOfProducts);
        else if(this.numberOfProducts <= MEDIUM)
            return this.CalculateMediumHours(this.hours["medium"], this.numberOfProducts);
        else
            return this.CalculateLargeHours(this.hours["large"], this.numberOfProducts);
    }

    protected abstract int CalculateSmallHours(int hours, int numberOfProducts);
    protected abstract int CalculateMediumHours(int hours, int numberOfProducts);
    protected abstract int CalculateLargeHours(int hours, int numberOfProducts);
}

次に、計算が特に複雑な場合は、それを戦略オブジェクトに抽出して、その特定のサブクラスだけに使用できます。

編集:任意の数の計算をサポートする場合は、時間の「カテゴリ」とそれぞれの計算の間のマッピングを管理するクラスを作成できます。次に、各サブクラス (または一部のファクトリ) は、各カテゴリに関連する計算を提供できます。

public class HoursCalculationStrategyCollection
{
    private readonly Dictionary<string, int> hours;

    private readonly Dictionary<string, Func<int, int, int>> strategies;

    public HoursCalculationStrategyCollection(IDictionary<string, int> hours)
    {
        this.hours = hours;
        this.strategies = new Dictionary<string, Func<int, int, int>();
    }

    public void AddCalculationStrategy(string hours, Func<int, int, int> strategy)
    {
        this.strategies[hours] = strategy;
    }

    public int CalculateHours(int numberOfProducts)
    {
        string hoursKey = null;

        if(numberOfProducts <= SMALL)
            hoursKey = small;
        else if(...)
            ...

        Func<int, int, int> strategy = this.strategies[hoursKey];
        return strategy(this.hours[hoursKey], numberOfProducts);
    }
}
于 2010-05-05T12:15:49.400 に答える
0

ファクトリーと戦略パターンを組み合わせることができます。次に、ファクトリは具体的なサービスを作成し、さまざまなサイズ (小、中、大) を処理するための戦略を渡します。

これにより、Service、Analysis、Writing、MediumStrategy、SmallStrategy、LargeStrategy、および ServiceFactory + 戦略のインターフェースの 8 つのクラスが得られます。

ServiceFactory には、使用する戦略を決定するためのコードが含まれます。何かのようなもの:

Analysis createAnalysis(int numberOfProducts) {
    SizeStrategy strategy;
    if (numberOfProducts <= SMALL) {
        strategy = new SmallStrategy();
    } else if (numberOfProducts <= MEDIUM) {
        strategy = new MediumStrategy();
    } else {
        strategy = new LargeStrategy();
    }
    return new Analysis(numberOfProducts, strategy);
}

この場合、保存できるコードはごくわずかです。演習としてはもちろん問題ではありませんが、実際にこれをリファクタリングするのに時間を無駄にすることはないと思います。

編集: 考え直して、ルールが変更される可能性が高いと仮定すると、おそらく OOP パターンよりも制御テーブルの方が適切であるように思われます。

于 2010-05-05T12:03:33.220 に答える