2

タイトルが私の問題をうまく説明していない可能性があります。誰かがそれをより適切なものに編集できれば幸いです。いずれかの方法:

与えられた製品価格を返すはずのコンポーネントを取得しましたid。次のようなインターフェイスを実装します。

interface IProductPriceFetcher
{
    double GetPrice(int id);
}

現在、価格は3 つの異なるソースから取得できます。

  • ウェブサービス
  • ウェブサイトのソースコードから直接 (スクラップ)
  • 最後のフォールバック (Web サービスと Web サイトの両方にアクセスできない) として、ローカル データベースからの最新の価格が返されます。

この 3 つの異なるソースの問題を回避するために、次のようなクラスを実装しました。

class MainFetcher : IProductPriceFetcher
{
    public double GetPrice(int id)
    {
        var priceFetcher = this.factory.GetWebServiceFetcher()
                        ?? this.factory.GetWebsiteFetcher()
                        ?? this.factory.GetLocalDatabaseFetcher();
        return priceFetcher.GetPrice(id);
    }
}

もちろん、ファクトリからの各メソッドは戻りますIProductPriceFetcherが、最初の 2 つは失敗して戻る可能性があることに注意してくださいnull。私GetLocalDatabaseFetcherは常に意味のあるオブジェクトを返すと仮定しました。

私の「一般的な疑問...メン」

Web サービス/Web サイトの呼び出しが成功したら、将来のフォールバック ケースとして、取得した価格をローカル データベースに挿入したいと考えています。私の質問は、上記のコードのどの部分がそれを担当する必要があるかということです。価格を返す具体的な Web フェッチャーの 1 つにすべきでしょうか? または、「アグリゲーター」フェッチャー ( MainFetcher) は、価格のソースが何であるかについての知識も持っているため? イベントを発生させる必要がありますか?DB 呼び出しでさらに別のインターフェイスを挿入しますか? デザインをより良いものに変更しますか?

なぜそれが私の問題として発生したのですか?さて、私はコードをきれいに保とうとしました (心配しないでください。これは私の空き時間のためだけのペット プロジェクトです - まさにこのような問題を解決するためのものです) おそらく SRP/SoC を念頭に置いて。今、私はこの考え方から切り替えるのに問題があるようです-つまり、ウェブページを取得する何かがデータベースの挿入も行っている可能性があるのでしょうか? ああ、さあ!:)

4

2 に答える 2

2

超分離設計が必要な場合は、次のようなデコレータクラスを実装し、それを使用してWebServiceFetcherとWebsiteFetcherの両方をラップします。

class DatabaseCachingFetcherDecorator : IProductPriceFetcher
{
    private readonly IProductPriceFetcher innerFetcher;

    public DatabaseCachingFetcherDecorator(IProductPriceFetcher fetcher)
    {
        this.innerFetcher = fetcher;
    }

    public double GetPrice(int id)
    {
        double price = this.innerFetcher.GetPrice(id);

        if (price != 0) // or some other value representing "price not found"
        {
            SavePriceToDatabase(id, price);
        }

        return price;
    }

    private SavePriceToDatabase(int id, double price)
    {
        // TODO: Implement...
    }
}

次に、ファクトリは次のメソッドを実装します。

public IProductPriceFetcher GetWebServiceFetcher()
{
    return new DatabaseCachingFetcherDecorator(new WebServiceFetcher());
}

public IProductPriceFetcher GetWebsiteFetcher()
{
    return new DatabaseCachingFetcherDecorator(new WebsiteFetcher());
}

この設計により、実際のフェッチャーがキャッシュメカニズムから切り離されます。

編集:工場出荷時にNULL値を返すのではなく、価格を取得できなかった場合にGetPriceメソッドが何らかのNULL値を返すと想定したため、この回答でデザインを少し読み間違えました。工場の責任はオブジェクトを確実に返すことなので、工場がNULLを返すのは少し臭いと思います。「価格が見つかりません」を考慮して、メソッドインターフェイスを変更しGetPriceておそらく返すようにすることを検討します。double?

于 2011-05-16T16:45:20.767 に答える
1

「キャッシュ」が必要な場合のように思えます。キャッシングは通常、実装に注入する一種のアスペクトまたは依存関係としてFetcher実装されます。IPriceCache以下では、ある種のインターフェースを想定してIDictionaryいますが、もちろん、必要な抽象化を挿入することもできます。また、価格フェッチャーのデータ ソースを抽象化することをお勧めします...:

class MainFetcher : IPriceFetcher {

 IEnumerable< IPriceSource > mSource;
 IPriceCache mCache;

 public MainFetcher( IEnumerable< IPriceSource > pSource, IPriceCache pCache )
 {
     mSource = pSource;
     mCache = pCache; 
 }

 public double GetPrice(int pID)
 {
     double tPrice;
     // get from cache
     if (mCache.TryGet(pID, out tPrice) {
         return tPrice;
     } else {
         // throws if no source found
         tPrice = mSource
             .First(tArg => tArg != null)
             .GetPrice(pID);
         // add to cache
         mCache.Add(pID, tPrice);
     }
 }
}
于 2011-05-16T17:18:15.157 に答える