MVC打破了封装吗?

时间:2013-03-14 16:36:10

标签: oop model-view-controller language-agnostic encapsulation

假设我有一个模拟城市的课程。其特点如下:

  • 它只有两个属性“name”和“population”,都是私有的,在构造函数中设置。
  • 它有这些属性的getter,但不是setter。

我不希望此类的任何用户设置属性,我希望他们使用公共的.edit()方法。

这种方法需要打开一个表格来输入城市和人口的新名称,即:一个视图。然后,如果我有一个视图,我想实现MVC模式,所以想法是控制器接收.edit()调用,呈现视图,检索数据,并将其发送到视图,以便它改变了它的状态。

但是,如果我这样做,我必须将城市模型的属性从私有更改为公共。因此,如果任何用户实例化我的类,她/他可以直接更改属性。

所以,哲学问题:这不是打破封装吗?

编辑只是为了让它更明确:

这个city_instance.edit()方法应该是改变对象的唯一方法。

此外,我发现我的部分问题来自误解一个模型是一个对象(你可以在php mvc框架上阅读),当它实际上是一个不同的抽象时,它是一个对业务逻辑进行分组的层(域对象+我猜更多的东西)

2 个答案:

答案 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的必然结果。