OOP背后的主要思想是统一单个实体(对象)中的数据和行为。在过程编程中,有数据和修改数据的单独算法。
在“模型-视图-控制器”模式中,数据和逻辑/算法分别放置在不同的实体中,分别是模型和控制器。在等效的OOP方法中,模型和控制器不应该放在同一逻辑实体中吗?
OOP背后的主要思想是统一单个实体(对象)中的数据和行为。在过程编程中,有数据和修改数据的单独算法。
在“模型-视图-控制器”模式中,数据和逻辑/算法分别放置在不同的实体中,分别是模型和控制器。在等效的OOP方法中,模型和控制器不应该放在同一逻辑实体中吗?
Answers:
MVC是UI结构的关注点分离中的一项练习。这是一种防止由于呈现未与内容分离而在用户界面中发生的复杂性的方法。
从理论上讲,所有对象都可以具有对包含的数据进行操作的行为,并且数据和行为保持封装状态。实际上,给定的OOP对象可能具有或不具有与其数据相对应的逻辑,或者根本没有任何逻辑(例如,数据传输对象)。
在MVC中,业务逻辑进入模型,而不是控制器。控制器实际上只是将视图和模型粘合在一起的一种过渡。因此,在模型中,您可以将数据和行为放在同一位置。
但是,即使这种安排也不能保证严格的数据/行为融合。仅包含数据的对象可以由仅包含逻辑的其他类进行操作,这是OOP的完全可接受的用法。
我给你一个具体的例子。这有点做作,但是假设您有一个Currency
对象,并且该对象可以用与美元挂钩的任何可用货币表示自己。因此,您将拥有如下方法:
public decimal Yen { get { return // dollars to yen; } }
public decimal Sterling { get { return // dollars to sterling; } }
public decimal Euro { get { return // dollars to euro; } }
...并且该行为将与Currency对象一起封装。
但是,如果我想将货币从一个帐户转移到另一个帐户或存入某种货币怎么办?该行为也将封装在Currency对象中吗?不,不会。钱包中的钱不能将自己从钱包中转入您的银行帐户;您需要一个或多个代理商(柜员或ATM)来协助将这笔钱存入您的帐户。
这样,该行为将被封装到一个Teller
对象中,并且将接受Currency
和Account
对象作为输入,但是它本身将不包含任何数据,除了可能Transaction
会帮助处理输入对象的一些局部状态(或对象)。
Teller
放在哪个实体/包装中?是Controller
从哪里Teller's
调用方法的,还是Model
因为是业务逻辑的一部分?
Teller
进入Model
,尽管可以从控制器调用它。它是业务领域的一部分。
MVC工作在很多抽象比单个对象的较高水平,而事实上这三个(模型,视图和控制器)将通常由多个对象,每个有两个数据和行为。
封装数据和行为的对象通常是程序的一个很好的基本构建块,并不意味着它是所有抽象级别和所有目的的最佳模式。
OOP并不限制每个都有自己的数据和行为的对象之间的交互。
想一想一个蚂蚁和一个蚁群的类比:单个蚂蚁的行为(整日奔跑,带来食物)不同于整个殖民地的行为(找到最理想的地方,制造更多的蚂蚁)。MVC模式描述了所需的蚁群社会结构,而OOP则指导了单个蚂蚁的设计。
OOP还涉及关注点分离,即分离不同对象中的不同角色/职责。
MVC分为以下几个部分:
因此,这些责任显然是不同的,并且确实应该分为多个实体。
在“模型-视图-控制器”模式中,数据和逻辑/算法分别放置在不同的实体中,分别是模型和控制器。
模型和控制器是两个不同的角色。模型既具有状态又具有逻辑,而控制器既具有状态又具有逻辑。他们进行通信的事实并不会破坏两者的封装-控制器不知道或不在乎模型如何存储其数据,或者在控制器检索或更新数据的某些部分时模型对数据的处理方式。模型不知道或不在乎控制器如何使用模型提供的数据。
这样想:如果对象不能在不破坏封装的情况下来回传递数据,那么实际上您只能有一个对象!
在等效的OOP方法中,模型和控制器不应该放在同一逻辑实体中吗?
MVC 是一种OOP方法-具体地说,它是决定如何使用对象有效组织程序的秘诀。而且没有,模型和控制器应该不是同一个实体。控制器允许模型和视图之间的分离。保持模型和视图彼此独立,使其更具可测试性和可重用性。
模型层不仅是数据,而控制器层仅仅是逻辑。
出于其目的,控制器层将具有完整的对象集合。将有一些对象用于从视图中接收输入,以及将输入转换为模型可以处理的形式。Struts Java框架在其Action / Form模型中有一个很好的例子。表单将使用用户的输入进行填充,然后传递给操作。动作将获取该数据并使用它来操纵模型。
同样,模型层也不是完全由数据组成。例如,以一个User对象为例,您可能需要从数据库中获取用户的代码,或者需要将用户与订单相关联或验证用户的地址在公司服务范围内的代码...您将获得图片。这不是控制器逻辑。它是业务逻辑,导致许多人将其Model层划分为多个层,例如业务逻辑的Service或Manager层,用于数据库访问的DAO(数据库访问对象)层等。
MVC并不是组织单个Model操作的方法。它的工作水平更高,它是一种组织如何访问应用程序的方法。视图用于显示数据和用于操作数据的人工操作,控制器用于在用户操作和各种视图之间进行转换,模型用于存储业务数据及其存在的业务原因。
我相信您将绑定到模型对象的持久性数据与模型进行交互的数据库中的应用程序数据混淆了。模型包含用于处理数据库和进行事务的业务逻辑和规则。它可能会设置和检查内部状态标志,例如今天是否有销售,用户是否具有VIP资格,然后在需要访问,设置或操纵数据或进行购买时相应地分支逻辑。当我们讨论对象封装方法和持久性值或数据的封装时,就是在谈论这些标志。
正如模型对象维护用于建立正在运行的业务规则的数据一样,IMO控制器应保留与应用程序的行为有关的更通用的应用程序状态数据,例如用户是否登录或是否拥有有效信用卡数据到位。模型方法将首先确定这些事物的状态,但是对于控制器来说,如果它们不适用于业务运营或数据交易方式,则维护与通用应用程序流相关的标志是有意义的。一旦确定它们尚未登录,就不要再通过用户状态检查来打扰模型,直到明确进行另一次登录尝试为止。
同样,使用适当的视图对象与在大多数服务器端Web框架中看到的更典型的HTML模板也是如此。加载用户的颜色首选项后,应该是保留该数据并在其上执行的视图。加载,验证和更改设置都是模型问题,但是只有在发生更改之前它们才是模型问题。
IMO,没有什么说控制器不能是将视图和模型作为内部聚合对象的复合对象。如果您像UI小部件工厂那样在较小的规模上应用MVC,这实际上是有道理的,因为控制器是将接口暴露给更高级别的应用程序对象的理想场所,同时掩埋了视图和模型如何交互的数据和逻辑细节。对于控制器应用程序实际上是最高级别对象的单一应用程序对象,这实际上没有任何意义。
不反,MVC也不需要OOP。
因为通常由类表示的控制器不保存任何数据。对于那些纯函数就足够了。
例如,如果您进一步将数据与行为分开,则可以说模型仅适用于数据库数据,每次调用它们的函数(负责数据操作)时,模型便会提取这些模型(而不是在实例中存储某种数据)字段)-然后您可以对模型说相同的内容。
更进一步,如果您采用应用程序的视图层并以类似的方式对其进行划分,那么您实际上将得出结论,即MVC与OOP无关,并且完全有可能仅使用过程方法来编写MVC实现而不会费劲。