模型的哪个部分应处理数据库插入

本文关键字:处理 数据库 插入 个部 模型 | 更新日期: 2023-09-27 17:56:40

Title可能不太能很好地描述我的问题,如果有人可以将其编辑为更合适的内容,我会很高兴。无论如何:

我得到了一个组件,该组件应该根据其id返回产品价格。它实现了如下所示的接口:

interface IProductPriceFetcher
{
    double GetPrice(int id);
}

现在,可以从 3 个不同的来源获取价格:

  • 网络服务
  • 直接来自网站源代码(报废)
  • 作为最终回退(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,但请注意,前两种方法可能会失败并返回null;我假设GetLocalDatabaseFetcher总是会返回有意义的对象。

我的"将军想知道...ment"

成功调用网络服务/网站后,我希望将获取的价格插入本地数据库,作为未来的后备情况。现在我的问题是:上面的代码的哪一部分应该对此负责?它应该是返回价格的具体网络获取器之一吗?或者"聚合器"获取器(MainFetcher),因为它也知道价格的来源是什么?我应该提出一些活动吗?通过数据库调用注入另一个接口?将设计更改为更好?

为什么它甚至对我来说是一个问题?好吧,我试图保持代码干净(不用担心,这只是我业余时间的宠物项目 - 正是为了解决这样的问题),可能考虑到 SRP/SoC。现在我似乎在从这种心态切换时遇到了问题 - 我的意思是,获取网页的东西怎么可能也在进行数据库插入?拜托!:)

模型的哪个部分应处理数据库插入

如果你想有一个超级解耦的设计,我会实现一个如下所示的Decorator类,并使用它来包装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());
}

此设计将实际提取程序与缓存机制分离。

编辑:我用这个答案稍微误读了你的设计,因为我假设如果无法获取价格,GetPrice 方法将返回某种 NULL 值,而不是工厂返回 NULL 值。我认为工厂返回 NULL 有点气味,因为工厂的责任是可靠地返回对象。我会考虑更改您的GetPrice方法接口以返回double?,以允许"找不到价格"。

在我看来,如果您需要"缓存",这听起来像是。缓存通常作为注入到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);
     }
 }
}