我目前有一个类Message_Repository
,其方法如下:
getLocationDetailsByID($messageId),
getCustomerDetailsById($messageId),
getMessages(),
updateMessageForEmail(array $data), //this takes an array which includes the message ID
getLinkIndicatorById($messageId),
setIndicator($data) //this takes an array which includes the message ID
这些方法都是数据库交互,在大多数情况下我选择的是各种数据,在某些情况下我正在进行更新。
我发现我正在充实,我的班级名称不再代表课堂上发生的事情,至少不是唯一的。
最佳做法是说一个Location_Repository
类和一个Customer_Repository
类,其中包含获取与主题相关的数据的方法吗?
仍在努力理解单一责任。理论上它看起来很简单,但在实践中我发现它更具挑战性,特别是当我的课程不断发展和变化时。绝对需要重构,但想确保我正确地考虑这个问题。
修改 我可能会对我班级的名字感到困惑。它不是严格意义上的设计模式的“存储库”,而只是我用来与数据库交互的类的命名约定。可能需要重命名它。
答案 0 :(得分:2)
存储库应该只是管理实体或domain objects,它们是同一个类的实例。在您的特定情况下,您应该在Message
实例,Location
实例和Client
实例中至少拆分三个单独的存储库。对于任何“链接指标”,可能还有第三个存储库。
此外,存储库类为not were your SQL code goes。
对于SRP,代码遵循的最佳描述是:“当类只有一个改变的理由时”。
如果你看一下福勒的文章,你会注意到,存储库实际上与它自己的多个依赖项进行交互。 SQL应该包含一个或多个data mappers
答案 1 :(得分:2)
Single responsability是雾化的OOP概念,导致Encapsulation concept。
SOLID Principle这些概念受到compounded的约束,这是一个强大的原则,可以节省你的大部分时间,避免让你的余生头疼:
单一责任原则:一个班级应该只有一个 责任(即软件中只有一个潜在的变化) 规范应该能够影响类的规范)
开放/封闭原则“软件实体......应该是开放的 延期,但因修改而关闭。“
Liskov替换原则“程序中的对象应该是 可以替换它们的子类型实例而不改变它 该计划的正确性。“另见合同设计。
接口隔离原则“许多客户端特定的接口是 优于一个通用界面。“
依赖倒置原则应该“依赖于抽象, [没有]结核。“
如果不了解您的所有业务规则并仔细阅读您的代码,就很难分析您应该如何整理软件,但我会给出一个镜头:
MessageRepository不会扩展客户或位置,但会由扩展。
opposing Uncle Bob perspective 在上图中,来自维基百科的作品toke的一个例子
在MessageRepository的构造中,您将在MessageRepository中实例化这些要使用的类。完美的情况是避免在类中有公共变量,并且深深地依赖于方法来检索和设置数据(我看着你已经以这种方式做了一些事情)。所以,到目前为止,我可以通过方法名称理解:
MessageRepository方法:
LocationRepository方法:
CustomerRepository方法:
记住这一点很重要:您的业务规则方法应该是私有的。
可读性建议:
关于命名,这是一个很好的问题。我建议你学习设计模式/软件架构,我最喜欢的一个(Model-View-Controller)是MVC()架构:
{{3}}
您的存储库'具有业务规则原则和数据库抽象的应该是一个模型,因此,如果你想使用该模式,创建MVC目录结构并努力实现它!
答案 2 :(得分:1)
通常我会将它们拆分,因此您可以为每种模型类型设置一个存储库。例如,返回“位置”模型的所有方法都应位于“位置”存储库中。与Update方法类似,将传递“位置”模型进行更新。
答案 3 :(得分:1)
如果我们将Doctrine存储库作为标准,那么几乎是的,您将拥有每种数据类型的存储库。
Message_Repository::getLocationDetailsByID();
可能会成为:
Location_Repository::getLocationDetailsByMessageID();
每个存储库都会获取单个职责,以获取与其自身数据类型相关的数据。
在Doctrine中,EntityRepository :: createQueryBuilder()通过->select(/* own data type */)
预加载构建器有点隐含。
但是每个存储库都不同,您可以将数据类型分组为"概念"并且在坚持SRP的同时处理它们,但随着应用程序的增长,最好将它们分开。