模型的哪一部分应该处理数据库插入?

发布于 2024-11-07 09:41:13 字数 1195 浏览 4 评论 0原文

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

我得到了一个组件,根据其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 ,附带说明前两个可能会失败和返回;我假设 GetLocalDatabaseFetcher 总是会返回有意义的对象。

我的“一般疑问... ment”

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

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

Title probably doesn't describe my problem too well, I'll be glad if somebody could edit it to something more appropriate. Anyways:

I got a component that is supposed to return product price given its id. It implements an interface like this one:

interface IProductPriceFetcher
{
    double GetPrice(int id);
}

Now, the price can be fetched from 3 different sources:

  • web service
  • directly from website source code (scrapping)
  • as a final fallback (both webservice and website are inaccessible), most recent price from local database is returned

To play around this 3-different-sources issue I've implemented class like this:

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

Each method from factory returns IProductPriceFetcher of course, with side note that first two can fail and return null; I assumed GetLocalDatabaseFetcher will always return meaningful object tho.

My "general wondering... ment"

Upon successful webservice/website call, I want fetched price to be inserted into local datbase, as a future fallback case. Now my question is: which part of code above should be responsible for that? Should it be one of the concrete web fetchers that returns price? Or "aggregator" fetcher (MainFetcher), as it also has knowledge over what's the source of price? Should I raise some event? Inject yet another interface with DB calls? Change design to better?

Why did it even occur as an issue to me? Well, I tried to keep the code clean (worry not, it's only pet project for my spare time - exactly for solving problems like this) possibly with SRP/SoC in mind. Now I seem to have problems switching from this mindset - I mean, how could possibly something that fetches webpages also be doing database inserts? Oh, come on! :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

灼痛 2024-11-14 09:41:13

如果你想要一个超级解耦的设计,我会实现一个 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? 也许,以允许“未找到价格”。

If you want to have a super-decoupled design, I would implement a Decorator class like the following and use it to wrap both the WebServiceFetcher and the 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...
    }
}

Then your factory would implement the following methods:

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

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

This design decouples your actual fetchers from your caching mechanism.

EDIT: I misread your design slightly with this answer, as I assumed that the GetPrice method would return some sort of NULL value if the price couldn't be fetched, rather than the factory returning a NULL value. I think the factory returning NULL smells a bit, since a factory's responsibility is to reliably return objects. I would consider changing your GetPrice method interface to return a double? perhaps, to allow for "price not found".

行雁书 2024-11-14 09:41:13

在我看来,这就像你需要一个“缓存”。缓存通常作为一种方面或依赖项实现,您可以将其注入到 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);
     }
 }
}

It sounds to me like if you needed a "Cache". Caching is typically implemented either as a kind of aspect or dependency which you inject into your Fetcher implementation. Below I assume IPriceCache with sort of IDictionary interface, but you could of course insert whatever abstraction you require. I also suggest to abstract away the data sources for price fetchers...:

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);
     }
 }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文