假设我有一个模拟城市的课程。其特点如下:
我不希望此类的任何用户设置属性,我希望他们使用公共的.edit()方法。
这种方法需要打开一个表格来输入城市和人口的新名称,即:一个视图。然后,如果我有一个视图,我想实现MVC模式,所以想法是控制器接收.edit()调用,呈现视图,检索数据,并将其发送到视图,以便它改变了它的状态。
但是,如果我这样做,我必须将城市模型的属性从私有更改为公共。因此,如果任何用户实例化我的类,她/他可以直接更改属性。
所以,哲学问题:这不是打破封装吗?
编辑只是为了让它更明确:
这个city_instance.edit()
方法应该是改变对象的唯一方法。
此外,我发现我的部分问题来自误解一个模型是一个对象(你可以在php mvc框架上阅读),当它实际上是一个不同的抽象时,它是一个对业务逻辑进行分组的层(域对象+我猜更多的东西)
答案 0 :(得分:3)
免责声明:我真的不明白你在哪里提出要实施的.edit()
方法,所以如果你能在那里澄清那一点会有所帮助。
这里要考虑的第一件事是,在您的问题的项目符号列表中,您似乎暗示City
实例就像一个不可变对象:它在构造函数中获取其实例变量,并且不允许任何人在外面改变它们。但是,您稍后声明您实际上想要创建一种可视化编辑City
实例的方法。这两个要求显然会产生一些紧张,因为它们是对立的。
如果你采用MVC方法,通过将视图与模型分开,你有两个主要选择:
City
对象视为不可变对象,而不是在表单中更改值时编辑实例,而是丢弃原始对象并创建一个新对象。City
实例的方法。如果您实际上将City
视为不可变对象,则第一种方法可以保持模型的完整性。对于第二种方法,有许多不同的方法:
City
类中提供一个mutator。这可以具有每个属性的独立setter的形状或公共消息(我认为这是您提到的.edit()
方法)通过获取数组一次更改许多属性。请注意,这里不要将表单对象作为参数,因为模型不应该知道视图。如果您希望视图记录模型中的内部更改,请使用Observer模式。这三种方法中的第一种是唯一与语言无关的选择。是否打破封装有点难以说明,因为需求本身会产生冲突(这基本上意味着希望将模型与视图分开,可以由用户更改但不允许模型本身为外面改变。但我同意,如果你想要可变实例,将模型与视图分开可以促进显式变异机制。
HTH
答案 1 :(得分:1)
注意:我指的是MVC,因为它适用于Web应用程序。 MVC可以应用于多种应用程序,并且它以多种方式实现,因此很难说MVC做或不做任何具体的事情,除非你严格谈论模式定义的东西,而不是特定的实现
我认为你对“封装”是什么有一个非常具体的看法,并且该观点与封装的教科书定义不一致,也不同意它的常见用法。没有“封装”的定义,我发现这需要没有制定者。实际上,由于Setter本身就是用来“编辑”对象的方法,所以这是一个愚蠢的论点。
来自维基百科条目(请注意“喜欢getter和setter”的地方):
通常,封装是OOP(面向对象编程)的四个基本原则之一。封装是为了隐藏类中的变量或内容,防止未经授权的方使用。所以像getter和setter这样的公共方法访问它,其他类调用这些方法进行访问。
http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)
现在,这并不是说MVC不会破坏封装,我只是说你对封装是什么的想法是非常具体的,而不是特别规范。
当然,使用Getters和Setter可能会导致许多问题,例如返回列表,然后可以直接在对象本身之外进行更改。你必须小心(如果你在意)保持你的数据隐藏。您也可以用另一个集合替换集合,这可能不是您想要的。
德米特定律在这里比其他任何东西都更有意义。
但是这一切真的只是一只红鲱鱼。 MVC仅涉及GUI,GUI应尽可能简单。它在视图或控制器中应该几乎没有逻辑。您应该使用简单的视图模型将表单数据反序列化为一个简单的结构,可以用于应用于您喜欢的任何业务体系结构(如果您不需要setter,那么使用不具有set的对象创建业务层)使用setter并使用mutattors。
UI层中几乎不需要复杂的架构。 UI层更像是一个边界和网关,它将平面形式和HTTP命令的性质转换为您选择的任何业务对象模型。因此,它不会在UI级别上纯粹是OO,因为HTTP不是。
这称为阻抗不匹配,通常与ORM相关联,因为对象模型不容易映射到关系模型。 HTTP到Business对象也是如此。在这方面,您可以将MVC视为ORM的必然结果。