避免变化传播

时间:2012-07-31 18:15:26

标签: java model-view-controller design-patterns

首先抱歉我的英语不好:D 最近,由于软件工程考试,我正在深入研究设计模式,我必须将软件系统设计为此考试的项目。

我无法处理一个问题:如何将实体类(代表我的应用程序域的数据)与其他所有内容分离开来!

我的意思是,更具体一点,如果我有一个类Patient(具有自己的私有属性和公共getter和setter方法),这是一个实体类,我认为到目前为止我做对了:D

那么让我们说我选择使用MVC模式架构来设计我的程序,Patient会在模型中,这是正确的吗?

现在,处理该类的每个视图都会引用它:假设我有三个视图:第一个查看所有患者,另一个查看患者,最后一个注册新病人(实际上会有其他课程,我应该提交一些报告,但我认为现在这是无关紧要的。)

这些课程中的每一个都将知道患者的结构,以便显示其数据,例如他们将调用其getter方法并以某种方式显示数据。

我认为没有办法避免这种情况,因为视图在语义上与它必须显示的数据(=模型)相关联:我的意思是它必须知道数据是字符串,日期还是整数,它还必须知道它的语义(它在现实世界中代表什么);所以我认为仅删除对Patient类的引用没有多大意义。

现在问题的核心是: 如何防止类Patient中的更改传播到使用它的所有类(不仅是视图)?

我的意思是:让我们说String Cellular属性在String [] Cellular中发生了变化,因为需求发生了变化。我将不得不修改: 1)DB / DB接口 2)患者类 3)(最糟糕的是)每个使用患者的班级!!

如何避免这种情况? 我认为在一个好项目中,单个类中的更改不应该在使用该类的所有模块的一系列更改中传播,但我是初学者,我想不出一种方法来阻止这种情况。

我甚至无法想到为实体类使用稳定接口的好方法,因为需求的变化将反映在接口的变化中(我不认为接口是要改变的!)。

请帮助!!

4 个答案:

答案 0 :(得分:1)

首先想到的是,你说你必须改变一堆使用Cellular属性的类,这是一件坏事。

这实际上是强类型语言的功能之一。它确保类型在使用该属性的整个系统中保持一致。

例如,如果Cellular字段从String更改为String [],那么您的数据库模型当然也应该调整以反映从1-1关系到1-many关系的变化,因此您的数据访问对象(或ORM层配置)需要进行调整以反映变化。编译器不会让你逃脱它是一件好事。

另一个评论是,只需要调整使用Cellular字段的类。但在这里,这是一件好事。因此,如果您有一个用户界面来收集患者的手机号码,那么现在编译器会告诉您,您应该真正允许用户收集多个号码。

在设计软件时,花费大量时间尽可能正确地获取模型至关重要。并且知道需求会在某种程度上发生变化,因此不要害怕重构也非常重要。 Eclipse,NetBeans或IntelliJ等现代IDE可以代表您协助并完成大部分繁忙的重构工作。

答案 1 :(得分:0)

并非所有更改都可以隔离到模型或视图或持久层。但在您的情况下,您可以使患者模型具有通用的联系人字段列表。视图获取它们的列表并为用户填充一些可编辑的文本框。 DatabaseMapper通常也会保留这些字段。

因此,除非GUI需要更大幅度地改变,否则添加Cellular可以完全在模型中完成。除非你知道你需要,否则你不会开始一般地构建它,但你可以重构它。

答案 2 :(得分:0)

对于您的实体类,我认为您应该在定义所有业务实体之后将接口抽象保留到最后,并可以在它们之间推断出一个共同的行为模式。只有这样才能推断出它们之间的共同界面。但是你的实体课最终是具体的;试图进一步抽象它们毫无意义。

最终对这些具体类的更改将需要更改控制器/视图,并与它们进行交互。不要担心这个。一旦为每个实体定义了所有视图,就可以开始计算抽象,如果有的话。

如果您已经定义了具体实体,那么您可以开始抽象出视图。对于一个财务示例,假设我有一个Trade类。显然我可以有特定类型的交易,例如BondTrade,EquityTrade等。所以我也可以有一个TradeView,它可以有具体的视图,即BondTradeView,EquityTradeView等。你可以有更高的抽象层次,但最终,您将需要定义特定视图的具体类。

Look at the Facade Design pattern as an example.外观可用于根据具体实现创建视图。这是统一所有具体实体的一种方式。它将包含基于您的实体知道要创建哪个视图以及如何创建视图的逻辑。这将是一种简化对实体进行的传播更改的方法。

答案 3 :(得分:0)

好的以下声明:

  

这些课程中的每一个都将按顺序了解患者的结构   显示其数据,例如他们将调用其getter方法并显示   数据以某种方式。

只有一半真相。您的观点不应该知道Patient是什么。它可能有一个名为PatientView的东西,就像它运行的模型一样。因此,位于模型和视图之间的部分需要执行突变。这基本上是这样的:

public PatientView convertPatient(Patient patient)  
{  
    return new PatientView(patient.foo(),patient.bar(),etc);  
} 

函数的契约保证将PatientView对象返回给调用者,随后该类是由封装它的类定义的接口的一部分。这也具有不知道Patient是什么的观点的优点。

底层模型的任何变化,即驱动整个应用程序的东西,都会引起某种类型的连锁反应。转换器的使用有助于最小化触摸点,因为应用程序的任何部分都不应该真正知道Patient是什么,并且每个区域之间应该有一个连接器:视图,控制器等将患者转换为正在进行的操作期望该部分申请。