使用“单一责任原则”强制我的容器具有公共设置器
我正在努力遵循 SOLID 原则进行设计。我发现,当您使用“单一职责原则”(SOLID 的 S)时,您通常必须在数据容器和数据处理器之间划分类。例如,如果我有一个具有 5 个属性的类 person,它是从数据库读取的,而不是将所有内容都放在一个类中,我会创建一个包含这些属性的 Person 类,以及另一个从数据库读取该信息并创建 Person 的 PersonReader 类。
如果我这样做,我必须打开 Person 属性,以便 PersonReader 可以访问它们,但与将所有内容放入黑盒子并使属性只读相比,我的封装性要少。
我错过了什么还是这个原则的缺点?
提前致谢
编辑:我已将个人作者更改为个人读者,因为一开始就不需要将属性设置者公开。
I'm trying hard to design following the SOLID principles. What I've found is that when you use the "Single Responsibility Principle" (the S of SOLID) you usually have to split classes between the data containers and the data processors. For example If I have a class person with 5 properties that is read from DB instead of putting everything inside a class I create a Person class with the properties and another PersonReader class that reads that information from the database and creates the Person.
If I do that I have to open the Person properties so PersonReader could access them but then I have less encapsulation than putting everything inside a black box and making the properties only readable.
Am I missing something or is this a drawback of this principle?
Thanks in advance
EDIT: I've changed the person writer to a person reader because there was no need to make property setters public at the beginning.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
大多数数据访问策略(ORM 技术)都涉及您所描述的那种妥协。侵入性最小的实体在需要时(例如,当不存在公共设置器时)使用反射(或内省等)来填充实体。
话虽如此,如果您的实体只是数据容器(如在 贫血域模型 中),单一职责原则不应该让您真正关心,因为您的对象没有会被数据访问代码破坏(或干扰)的复杂逻辑。
Most data access strategies (ORM techniques) involve compromises of the kind you're describing. The least intrusive ones use reflection (or introspection, etc.) to populate your entities when they need to (when no public setters exist, for example).
Having said that, if your entities are only data containers (as in the anemic domain model), the single responsibility principle shouldn't really concern you much, since your objects don't have complex logic that would be spoiled by (or interfere with) data access code.
也许我遗漏了一些东西,但是为什么你不为 Person 类编写一个接受其所有属性的构造函数呢?
这样,PersonReader 将能够创建一个人,而无需您打开该人的属性。
另一种选择是将人员属性的 setter 标记为内部(并使程序集内部对 PersonReader 的程序集可见,如果它位于不同的程序集中)。这将允许您访问人员属性,但会阻止任何人在程序集外部更改它们。
Maybe I'm missing something, but why won't you write a constructor for Person class which accepts all it's properties?
This way the PersonReader will be able to create a person without you opening the person's properties.
Another option is to mark the setter of the person's properties as internal (And make the assembly internals visible to the assembly of the PersonReader, if it's in a different assembly). This will allow you to access the person properties, but will prevent anyone to change them outside of the assembly.
我绝对同意你的观点,有时你会遇到这个问题。我已经体验过序列化,这与 ORM 类似。就我而言,我需要将对象的状态写入(二进制)文件。
在某些情况下,创建一个可以写入和读取内部状态的接口可能是合适的。因此,您的 person 类将获得两种方法“保存”和“加载”(或“写入”和“读取”或您认为合适的任何方法)。这些方法分别传递“IPropertyWriter”和“IPropertyReader”。 (就我而言,我将它们称为 InStream 和 OutStream)。
Person::save(IPropertyWriter writer) 会这样做
你可以说你仍然违反了单一责任原则,但我认为没有其他人应该知道 Person 的内部结构。 (特别是在我的序列化情况下,我必须存储部分无法通过 getter 访问的内部状态)。要点是 Person 不与任何数据库代码耦合。您的 IPropertyWriter 可以是一切。此外,“了解其自身属性”的责任并不是真正的新属性,而是附加到每个对象上。
您可能还会问这个人改变的可能性有多大。如果不太可能,我认为 C++ 中的
friend
类是一个好方法,但如果它可能会改变,我宁愿在声明属性的地方使用序列化方法,以便您不要忘记更新相关代码。I definitely agree with you that sometimes you run into this problem. I've experienced it with serialization, which is similar to ORM. In my case I needed to write the state of an object to a (binary) file.
In some cases it can be appropriate to create an interface through which the internal state can be written and read. So your person class would get two metheds "save" and "load" (or "write" and "read" or whatever you think is appropriate). These methods are passed a "IPropertyWriter" and "IPropertyReader" respectively. (In my case I called them InStream and OutStream).
Person::save(IPropertyWriter writer) would then do
You can argue that you are still violating the Single Responsibility Principle, but I'd argue that nobody else should know the internals of Person. (Especially in my case of Serialization, I had to store the internal state which is partly not accessible through getters). The main point is that Person is not coupled to any Database code. Your IPropertyWriter can be everything. Furthermore, the responsibility for "knowing its own properties" is not really a new property but is attached to every object anyways.
You might also ask the question how likely it is that Person will change. If it's very unlikely, I think
friend
classes like in C++ are a good way, but if it's likely to change, I'd rather have the serialization methods right at the place where the properties are declared so you don't forget to update the dependent code.你想得太细了。特定的类应该读取数据库 - 但您不需要一个唯一的工作就是创建 Person 的类。那只是浪费。单个数据库读取器应该能够创建通过数据库的任何类 - 因为这就是它的工作。从数据库中读取。
打开 Person 的属性以便 DatabaseReader 可以访问它们不存在封装问题,因为如果没有类能够创建 Person,那么 Person 就是多余的。可创建和可破坏是任何类责任的固有部分。
You're thinking too granularly. A specific class should read the database- but you don't need a class whose sole job is to create a Person. That's just wasteful. A single database reader should be capable of creating any class that comes through the database- because that's it's job. To read from the database.
There's no encapsulation problems with opening Person's properties so that DatabaseReader can access them, as Person is redundant if no class is capable of creating it. It's an inherent part of any classes responsibility to be creatable and destructible.