Definitionofapattern:“Apatternaddressesarecurringdesignproblemthatarisesinspecificdesignsituationsandpresentsasolutiontoit”.
软件设计模式的定义:“设计模式处理一个特定设计情况下反复出现的设计问题,并且为其提供一个解决方案”。
软件设计模式适用于小范围的局部的程序设计
为什么要使用设计模式?
Definitionofasoftwarearchitecture:Thesoftwarearchitectureofaprogramorcomputingsystemisthestructureorstructuresofthesystem,whichcomprisesoftwarecomponents,theexternallyvisiblepropertiesofthosecomponents,andtherelationshipsamongthem.
软件体系结构的定义:程序或计算系统的软件架构是系统的一个或多个结构,包括:软件组件,外部可见性质,组件之间的关系。
软件体系结构是软件的整体架构,用于描述整个软件系统的结构
软件体系结构举例(JavaEEArchitecture)
ThreereasonswhySAisimportanttolarge,complex,software-intensivesystems.
软件体系结构(SA)对于大型复杂的软件密集型系统来说很重要的三个原因。
Itisavehicleforcommunicationamongstakeholders.
它是一种在项目参与者之间的一种交流工具
Itistherepresentationoftheearliestdesigndecisions.Itshowsthetradeoffs.---betweenperformanceandsecurity,---betweenmaintainabilityandreliability,and---betweenthecostofthecurrentdevelopmenteffortandthecostoffuturedevelopmentsinthearchitecture.
tradeoffs:折衷,权衡
它是最早的设计决策的代表。它展现了一种折衷和权衡:---在性能和安全之间---在可维护性和可靠性之间---在当前开发工作的成本和架构中未来开发的成本之间
Itisareusable,transferableabstractionofasystem.It---canbeappliedtoothersystemsexhibitingsimilarrequirementsand---canpromotelarge-scalereuseandsoftwareproductlines.
它是在一种可重复使用的、可转移的、抽象的系统,它---可以应用于其他具有类似要求的系统,以及---可以促进大规模重用和软件产品线
Creational
结构型模式包括:
结构型设计模式的主要目的是将不同的类和对象组合在一起,形成更大或者更复杂的结构体
例如,形成复杂的用户接口或者复杂的账户数据接口。
注意,该模式不是简单地将这些类摆在一起,而是要提供这些类之间的关联方式。
问题:一个层次类包含多个子类,当我们试图调用某个类中的某个方法,却不知道选择调用哪个类时怎么办?
工厂方法模式设计(Version1.0)
Main(){if(usrChoice==1){ obCar=newLincoln(m);} elseif(usrChoise==2){ obCar=newCadillac(m);} elseif(usrChoise==3){ obCar=newBuick(m);} obCar.buyCar();}该设计的缺点:高耦合、难看的条件语句、客户程序需要知道服务类的全部细节
工厂方法模式设计(Version2.0)
怎样改善1.0的设计?使用工厂方法模式,将选择与实例化(初始化)的一个适当类的功能封装在一个专门的类的专门的称作工厂方法的方法中==>委托
下面的类图表示一个设计---使用简单的工厂方法模式查询不同类型车险的特征。(使用简单工厂方法模式设计的汽车保险类)
AutoInsurance(汽车保险):各种索赔保险政策的接口(即describe())
四个子类
PolicyProducer:一个带有工厂方法的工厂类
源码
(1)下面的类图表示了工厂方法模式的设计,用于查询不同类型汽车保险的特性。
(2)汽车保险的例子
工厂层次类
接口类PolicyProducer.
源代码
1、中心不同
简单工厂模式的中心是一个具体的工厂类
工厂模式的中心是抽象工厂类(或接口)
2、工厂方法不同,一个静态,一个动态
在简单工厂方法模式中,工厂方法是静态的和
在工厂方法模式中,工厂方法是动态的,并且分布在具体的工厂类中
3、工厂方法的可扩展性
如果向产品层次结构中添加了一个新产品,那么我们只需要向产品层次结构中添加一个具体子类,向工厂层次结构中添加一个子类。
没有必要修改抽象工厂和现有的具体工厂子类方法(工厂类的已有源代码不需要改变)
4、两个工厂方法返回类型都是超类类型
问题:是否需要修改Client类?
在简单工厂方法模式和工厂方法模式中,工厂方法返回Product类型的对象,而不是具体产品类的对象。
客户端不需要知道类型的类型(这样,客户端类就不知道到底哪个产品子类的对象被创建了)
5、工厂方法模式支持开闭原则,但是简单工厂方法模式不支持开闭原则
(开闭原则:打开扩展,关闭修改)
客户类直接创建层次类的对象的情况。此时,1)客户类选择创建某个子类对象,2)并且对该对象的方法实施调用。
想法:1)客户类委托工厂类创建层次类的某个子类的对象2)客户类对返回的对象实施调用
1、简单工厂方法模式的实现
将参数p传递给工厂类,工厂方法根据此参数创建合适的类的对象,并且以Car类型返回给调用者
2、工厂方法模式的一种实现方法
优点:①客户类更干净了;②客户类调用工厂子类变得简单了缺点:不符合开闭原则
现在假设有多个结构相同的产品层次类
问题:我们是否仍然使用工厂方法模式?
一些想法
对于每个产品层次类,我们构造一个工厂层次类下图中使用的三个创建者
注释:这不是很好,因为有太多的Creator类——还有其他更好的解决方法吗?
新的解决方案——请注意以下产品结构相同
1、产品不同
(1)对于工厂方法模式,产品是一个单一产品类层次结构
(2)对于抽象工厂模式,产品是一组产品类层次结构
2、有关可扩展性
(1)工厂方法模式遵循开闭原则,同时
(2)抽象工厂模式只部分遵循开闭原则
2.5、关于开闭原则——两种情况
Case1
Case2
让我们设计一个web应用程序来查询不同类型建筑的特征。考虑两种类型的建筑:住宅和共管公寓。
此外,建筑可以是任何一种——SuperorMedium因为House和Condo代表了两种产品,每一种产品都可以分为super和medium,所以我们可以使用下面的抽象工厂模式
设计原因
设计好处
责任分离
选择和实例化适当的House/Condo实现者的责任可以从应用程序对象转移到单独的指定抽象工厂类。
关于client对象AbstractFactoryGUI类:
它使用实现BuildingFactory接口的具体工厂实例来创建对象表示不同类型和类别的房子,而不需要知道需要实例化的实际具体类。
Client对象不需要知道这些具体工厂类的存在。
AbstractFactoryGUI如何工作?
clientAbstractFactoryGUI不需要不同的具体的House/Condo类。
方法汇总(MethodSummary)
类的所有暴露给外界的方法的全体
类的接口提供外部视图
怎么把两个水管连接起来?不兼容的接口
转换了接口,从而可以将粗细不同的两个水管连接起来。在软件设计中,我们也经常会遇到类似的接口不一致的问题
publicclassClient{ privatestaticTargetadp;//usetheinterfaceastypepublicstaticvoidmain(String[]args){ adp=newAdapter();adp.operation1();adp.operation2();}}问题:在类适配器模式中能否同时适配两个类?如果有两个现有的类Adaptee1和Adaptee2,我们还能像下面这样使用类适配器模式吗
publicclassAdaptee{publicvoidoperation1(){System.out.println(“operation1isinAdaptee.”);}}publicinterfaceTarget{voidoperation1();voidoperation2();}怎么写Adapter类?
在使用适配器模式的时候Client类的代码
Targettgt; //==>面向接口编程tgt=newAdapter(); //==>tgt.operation1();tgt.operation2();原因:一个Target接口可能有多个不同的Adaper
有关适配器模式的讨论:哪种情况下使用(类、对象)适配器模式?
假设一个以美国客户为主的电商网站也为加拿大客户服务。已经设计实现了两个类:
问题:接口不相容。
领导决定客户类Customer类访问一个唯一的接口,验证美国地址与加拿大地址。
使用类适配器模式:使用一个适配器改变CAAddress的接口(1)需要设计一个名为AddressValidator的Java接口(2)需要一个适配器类CAAddressAdapter——继承CAAddress类、实现AddressValidator接口
在Customer中怎样使用该模式?
AddressValidaterav;if(userchoosesUS)av=newUSAddress();elseif(userchoosesCanada)av=newCAAddressAdapter();av.isValidAddress(addr,pcode,state);也可以使用对象适配器进行设计
图示比较
适配器模式的应用主要体现在两个方面(1)改变接口(2)增加功能
问题:这两个方面哪个是主要的呢?回答:改变接口。实际上增加功能也可以看作是改变接口,因为增加了功能也就改变了接口。
类适配器模式和对象适配器模式的区别(1)在类适配器模式中,所有属性和方法都是继承的(2)在对象适配器模式中,通常只选择一个或几个方法来拉入适配器类
好处:同一个接口Target,可以有不同的实现。例如,多种加密算法的实现。
将抽象部分与实现部分分开的军队机构设置
咖啡售卖机软件设计
flat设计(version1.0)
缺点:这个设计是扁平的,有一些重复
增加新的层次(version2.0)
重新设计:更好,但有三层。添加一个新的咖啡需要在两个地方添加新的子类例如:增加卡布奇诺可扩展性不好
问题:添加新的咖啡需要添加新的子类在两个地方将抽象部分与实现部分分开(version3.0)解决方案:将类层次结构划分为两个类层次结构
在Med或者Super类中,调用Latte或者Mocha(version4.0)
可扩展性增强了
在两个维度上可以自由地添加类,而不会影响到其它的类
考虑一个冰激凌销售机的例子,该机器销售系统包含两个维度:杯子大小与冰激凌品牌。
杯子的体积上暂时分为中杯(Mediumcup)、大杯(SuperCup);
在冰激淋的品种上,分为哈根达斯(Haagen-Dazs)、雀巢(Nestle)、冰雪皇后(DairyQueen)
EncryptedInfo1的加密算法:折叠算法
EncryptedInfo2的加密算法:分组互换算法
可以在两个维度上增加新的类而不影响其它的类
举例:排序程序设计
通过分别使用下列排序算法,设计一个程序对整数进行排序冒泡排序、堆排序、插入排序、快速排序
设计1:使用一个名为sort的类来实现所有排序算法
类图
评价:最愚蠢的设计问题:(1)增加新算法,需要重新编译整个类(2)修改算法,需要重新编译整个类(3)可复用性差
设计2:将类划分为两个类,一个Client类,一个Sorting类(包含所有所需的排序算法)
设计3:进一步拆分类:将类拆分成四个类
设计3实际上使用了策略模式的思想
什么时候使用策略模式?
(2)当要隐藏复杂算法的实现细节的时候当算法使用客户不应该知道的数据时。使用Strategy模式可以避免暴露复杂的、特定于算法的数据结构。
Context的实现方式
策略模式的工作机制
(1)客户端通常创建一个ConcreteStrategy对象并将其传递给Context
(2)Context将客户机的请求转发给它的strategy
(3)客户只与Context交互(目的)
策略模式优点
(2)容易扩展:封装算法在不同的策略类允许您更改算法独立于它的Context(独立于Context),使其更容易理解、切换(切换到更有效的算法)、扩展。当更改现有算法实现或向组中添加新算法时,现有策略类层次结构和Context都不受影响。
策略模式缺点
Client必须知道不同的Strategy——客户端必须了解策略的不同之处,然后才能选择合适的策略
排序问题——利用策略模式的新设计
应用策略模式的带有Context类的整数序列排序程序设计类图
在政府或商业网站上,许多数据需要用图表解释,用户可选择不同的图表以便方便地显示一些数据。例如显示全国各个行业的外贸出口比例等等。同一组数据,可以有不同的图表显示:条形图(Bar)、折线图(Line)、饼图(Pie)、XY散点图(XY(scatter))、面积图(area)等等图形。
为什么要使用策略模式进行设计?
假设在用户在输入一组数据以后,程序将为你根据需要画出你所需要的图表类型。各种图表,虽表现形式不同,但都显示同一组数据,功能是相同的。可以使用策略模式实现各种图表的显示。将该根据用户输入的一组数据,利用各种不同的图标显示。为简单起见,我们在本例子中,只使用条形图(Bar)和饼图(Pie)。
使用策略模式设计的绘图软件-省略Context类的情况
使用策略模式设计的绘图软件-交互情况
为什么在本设计中省略了Context类?
两个类BarChartGraph和PieChartGraph中,都有一个paintComponent(Graphicsg)方法。在该方法中,调用其超类JPanel的paintComponent(Graphicsg)方法。
在创建BarChartGraph或PieChartGraph的对象时,paintComponent(Graphicsg)方法将自动运行。
即,BarChartGraph或PieChartGraph的对象代表一个JPanel,且相应的图形,例如Barchart,已被绘制在该JPanel上。
为了将图形显示在GUI上,只要创建BarChartGraph或PieChartGraph对象并将其贴在GUI即可。
故省略了context类。
猴子案例
关于状态的转换的建议
状态模式的优点
信号灯有红,黄,绿三个状态。使用状态模式设计交通信号灯控制软件。
设计一个LightState抽象类,和3个具体的状态子类:Red、Yellow、Green
状态超类或者状态子类的任务拍照,统计车辆个数,记录违章车辆负责改变状态,红->绿->黄->红
Context对象提供交通灯所需要的颜色。
交通灯控制软件状态图
交通信号灯控制软件-设计1
关于主用户界面
ExtendsJFrame
自动更新图形界面:
利用JFrame类的paint()方法,该方法在TrafficLightGUI生成的时候,将会自动被调用。
在paint()方法中,使用了repaint()方法,该方法将自动调用将用paint()。
停顿效果:使用了Thread类的wait()方法。产生停顿效果,从而模拟了交通信号灯。
交通灯控制软件图形界面
本程序的交互情况
【在客户类中】客户类首先创建Red类的对象,在创建Context类的对象的时候,将Red类的对象以参数的形式传给Context类的对象cxt。rs=newRed();cxt=newContext(rs);【在客户类中】在状态类中还需要保持Context类的对象,因此需要调用rs.setupContext(cxt);将刚刚创建的Context对象cxt传递给状态类
【在客户类中】还需要给Context类的状态变量state赋予初始值cxt.setState(TrafficLight.RED);
【在客户类中循环调用】在方法runTrafficLights()中,使用语句color=cxt.getColor();cxt.doAction();反复调用Context类的方法,以便产生循环显示灯的颜色的效果
在客户类中循环调用——进一步解释
【在Context类中】每当调用cxt.doAction()的时候,相应地状态子类的doAction()将会被调用,changeState()也将会被调用【在状态类中】changeState()负责反复更新Context类中的state变量【在Context类中】Context类的setupStateObj()根据新的状态,决定使用某个状态子类对象light,light.performTask()
限制:在以上任何状态下,都不允许使balance低于超过透支上限(-1000)的交易发生。
存入2500美元的顺序图
BankContext类(Context类提供较高层业务逻辑)
State类
State子类
用户图形界面
公司类库中已经存在生物教学项目团队写的Animal层次类(version1.0)
网络游戏开发团队修改Animal层次类-造成了接口污染(version2.0)
网络游戏开发团队决定合理地利用Animal层次类(version3.0)
在美国,有很多种税。税可以通过使用如下的类层次结构来表示。对于每个类,税率和计算税收的算法都是不同的(version1.0)
改善设计:将具体的计算税收的方法从层次类中分离出来,而在层次类中保留数据维护方法(version2.0)一个可能的解决方案是:分离类的主要功能,即让“Tax”类独立于应用于它们的操作
进一步改善设计(version3.0)
访问者模式类图——无聚合结构的情况
访问者模式类图
访问者模式
需求
设计
设计:考虑到对象结构包含很多个子类;这些类有不同的接口;你需要针对不同的类进行不同的操作。所以我们采取使用访问者模式进行设计。
何时使用访问者模式?
(1)对象结构包含很多个子类,而这些类有不同的接口。你需要针对不同的类进行不同的操作。
(2)许多不同的且不相干的操作需要施加于对象结构,而你想要避免这些不同的操作污染这些类。
(3)对象结构类很少改变(例如税收的种类很少改变),但是你需要经常地在这些结构体上增加新的运算(税率可能经常改变)。
(4)注意,使用Visitor模式的客户端必须创建一个ConcreteVisitor对象,然后遍历对象结构,使用Visitor访问每个元素。当访问一个元素时,它调用与它的类相对应的Visitor操作。元素将自身作为此操作的参数提供,以便访问者在必要时访问其状态。
访问者模式的优点
(1)访问者使添加新操作(在访问者中)变得容易。访问者可以很容易地添加依赖于复杂对象组件的操作。只需添加一个新的访问器,就可以在对象结构上定义一个新操作。相反,如果将功能分散到许多类上,则必须更改每个类来定义一个新操作。
访问者模式的缺点
添加新的ConcreteElement类很困难。每个新的ConcreteElement都会在Visitor上产生一个新的抽象操作,并在每个ConcreteVisitor类中产生相应的实现ConcreteElement->功能
讨论:访问者模式的重点
什么是访问者模式的重点
注:类objectStructure类对于批量访问很重要。另外使用此类,可以使得客户类包含较少的条件语句。能够有效地做到责任分离。
结论:要写好accept()方法与objectStructure
(a)指挥起飞:所有的飞机必须接到起飞的命令才能起飞
(b)指挥降落:所有的飞机必须接到降落的命令才能降落
(1)面向对象应用程序由一些互动的对象组成通常,OO应用程序由一组对象组成,这些对象为了提供服务而相互交互。
(2)当参与对象数目较少时,对象之间可以直接交互这种交互可以是直接的(点对点),只要直接相互引用的对象的数量非常少。
ObjectA<==>ObjectB
我们经常这样做,例如:(1)在策略模式和状态模式里面,Context类的对象与Strategy层次类对象的耦合是双向的(2)在状态模式里面,Context类的对象与State层次类的对象之间的耦合是双向的
这么多物体的高耦合,(1)导致复杂的方法调用(2)影响应用程序的可维护性(如果你要增加一个新的类,就太麻烦了)(3)由于更高的耦合,大大减少了重用这些对象的范围
为了减少对象之间的紧密耦合,可以使用星形形式重新设计中介设计模式
怎样设计类图才能实现以上的逻辑图?(1)Mediator类应该与参与者类(对象A,B,C,…,H的类)之间有某种形式的关联,(2)而参与者类之间不应该有关联。
该设计类图由两部分组成,一部分是中介者类,另外一部分是以上的参与者对象。
程序的构件说明:
我们可以使用中介模式来减少对象之间的直接调用
中介模式表明抽象对象交互细节到一个单独的类,Mediator类负责所有对象交互
中介者模式的优点
中介者模式的缺点
使用中介可能会降低性能
酒店-航空公司-旅游信息系统
有关协作程序的Mediator模式实现,请参见下面的类图酒店(宾馆)、航空公司(航空公司)、旅游(旅游公司)。
目的:三家企业信息共享前台接待员在酒店信息GUI中输入客户信息时,航班信息GUI和旅游信息GUI会显示相同的信息,包括客户的名字、身份证号码、国籍这样三家公司就可以共享信息
初步设计:3个对象,Hotel,Airline,TourCompany这3个对象有以下关系。
设计缺点:各个对象之间直接交互——调用关系复杂——可扩展性差
解决方案:使用中介者模式,引入中介者类BusinessMediator
01编写中介类
privatehotelGui:hotelGui;privateairlineGui:airlineGui;privatetourGui:tourGui02编写注册方法
在中介类中,编写所有注册方法来注册所有参与的对象。例如,参与的对象类型应该分别包含在注册方法的参数中。
registerHotelGUI(HotelGUIhg)registerAirlineGUI(AirlineGUIag)registerTourGUI(TourGUItg)03在每个注册方法内部,将从参数传递的对象赋值给相应的私有成员。
//For,example,registerHotelGUI(HotelGUIhg){hotelGui=hg;}通过这种方式,参与的对象被“拉”到中介对象中。
主类的实现
创建中介对象。在客户端类中,应该创建中介类的对象创建参与者对象。然后,应该用刚刚创建的对象通过参数传入的方法创建所有参与类的对象。
设计一个海洋信息分析系统,暂时设计2个类(version1.0)
缺点:不间断的循环调用,浪费了CPU运行能力,效率很低。
改善设计:改善调用方式。当数据有变化时,OceanData对象通知DataAnalizer对象(version2.0)
最初设计(version1.0)
缺点:这种设计很难扩展
改善设计(version2.0)
改善设计的优点:松散的耦合
观察者模式对于设计一个在Observer和Subject之间一致的通讯模型很有用。
观察者模式对于设计一个一致的通信模型非常有用,观察者:一组依赖的对象和主体:他们依赖的对象
当你可以将一个程序中的对象分为主题与观察者的时候,可以使用如下的观察者模式进行设计。
(1)一个主题可以有多个观察者。一个实验对象可以有不止一个这样的观察者。每一个观察者都需要知道主体的状态何时发生变化。
(2)主题保持一个动态列表,记录已经注册的观察者主题必须保持一个动态注册观察者列表。(主题不能维护这样的观察者的静态列表,因为给定主题的观察者列表可能会动态更改)
(3)观察者模式所包含的必须的活动①观察者必须注册自己任何对主体状态感兴趣的对象都需要显式地将自己注册为主体的观察者。②Observable必须通知观察者当主体的状态发生变化时,它会通知所有注册的观察者。③观察者可以查询观察对象的状态在收到来自主题的通知后,每个观察者都会查询主题以使其状态与主题的状态同步。
(4)通知观察者模式的两种策略①pushmodel(将观察者所需数据全部推给观察者,例如:update(Objecta)):主体应该发送观察者可能感兴趣的状态信息。②pullmodel(主题提供接口,供观察者访问,例如:update(Observableobs)):主体应该提供一个接口,使观察者能够查询主体所需的状态信息来更新他们的状态。
温度转换程序设计的例子。
我们试着用摄氏、华氏和开氏来表示温度。我们使用一个TemperatureGUI供用户输入温度
CelsiusorFahrenheitorKelvin.然后,其他3个图形界面将以摄氏和华氏两种方式显示温度。
在这种情况下,我们可以使用观察者设计模式,将TemperatureGUI封装为实现可观察接口的类,封装CelsiusGUI,FahrenheitGUI和KelvinGUI作为3类实现接口观察者。
(1)观察者必须注册一个观察者可以通过调用observable中的register方法将自己注册到这个可观察对象中。
(2)发生事件,被观察者必须通知观察者notifyObservers()将通知观察者物体温度gui的状态发生了变化,然后CelsiusGUI,FahrenheitGUI和KelvinGUI对象将通过使用update(sObservable)做一些事情,在其中,如果有必要,将依次调用Observable来检查状态的变化。
利用观察者模式设计的温度转换程序的典型交互
设计分析
设计亮点:两个接口ObservableObserver
在TemperatureGUI中,你需要实现所有的方法:notifyObservers()register(obs:Observer)unRegister(obs:Observer)
在CelsiusGUI,FahrenheitGUI,andKelvinGUI中,需要实现方法:update(Observablesubject,Objectarg)
1)createobjectofobservableTemperatureGUItempObj=newTemperatureGUI();2)createobjectofobserverCelsiusGUIcg=newCelsiusGUI();FahrenheitGUIfg=newFahrenheitGUI();KelvinGUIkg=newKelvinGUI();3)registerobserverstotheobservabletempObj.register(cg);tempObj.register(cg);tempObj.register(cg);4)temperatureObj.notifyObservers(bTem);03Design2使用JavaAPI的Observer接口和Observable类。
使用JavaAPI提供的观察者机制——使用java内置的事件处理策略。ObservableclassandObserverinterface
实例类图
实例交互
利用Java提供的类与接口与观察者模式计的温度转换程序的典型交互
客户类
被观察类
该软件具有图形用户界面,可以显示汽车图片、汽车说明和当前拍价 a.用户首先在车的列表中选择一款车,然后点击Search按钮 b.该车的图片,描述,与当前拍价将在屏幕上显示 c.用户输入新拍价 d.新的拍价在另外一个视窗中显示关于数据显示:有三种显示方法
设计1:仅仅使用一个Java类实现全部功能
设计2:将整个项目设计为两个组件
用户界面类CarAuctionGUI
1、提供用户输入功能2、显示查询结果类AuctionFunctions
实现了责任分离
1、在用户界面程序中,除了搭建所有的图形组件外,仅仅提供了用户输入(和简单的输出功能)的获取功能: getSelectedCar():String getBitPrice():String2、将所有其它的功能都放到单独的一个类AuctionFunctions中缺点
在一个类AuctionFunctions中,集中了业务逻辑、所有的辅助功能,造成了接口污染设计:MVC设计模式
新设计方案考虑将来(追加)可能扩展的功能:
1、要求在输入方面有可扩展性,例如键盘输入,图形界面上的按钮输入,手机、平板的触摸屏输入2、对于汽车信息,允许有不同的显示:图片显示,文字显示,性能比较、分析图形,销售情况(柱形图,饼形图)3、希望将所有的业务逻辑都封装到一个类中解决方案:使用MVC(Model-View-Controller)设计模式进行设计
定义:在软件工程中,模型-视图-控制器(MVC)是一种架构模式,它将用户和应用程序之间的交互分为三个角色:
组件:模型、视图、控制器
连接器:显式调用、隐式调用或其他机制(例如HTTP协议)
Model(模型)的责任
View(视图)的责任
Controller(控制器)的责任
三者之间的关系
数据更新:改变-传播机制(change-propagation)
如果用户通过一个view的controller改变了model,所有的view必须反映出该改变。
当数据发生变化的时候,model通知所有的view,告诉他们数据已经改变了。
Views可以遍历(访问)model中的数据,以便发现到底是什么改变了。然后更新显示数据。
注意,改变-传播机制保证了用户界面和模型的一致性。可由观察者模式实现。
MVC架构在以下的架构中均有所体现 J2EE: Struts SpringMVC PHP CakePHP Strusts4php C#.NET Girders三、MVC设计模式实例01网上二手车拍卖系统的设计方案采用MVC模式-使用观察者机制的情况,用户输入界面和两个显示视图分别独立显示
将事务逻辑部分和用户界面部分分开,采用MVC模式。设计如下:
CarGUIInfoView和CarBitInfoView实现了interfaceObserver,实现了方法update()。
每个CarGUIInfoView和CarBitInfoView都将自己注册为CarModel的观察者。每当CarModel的状态改变的时候,其方法notifyObservers将改变告诉给它所有的观察者,观察者的update()可以自动查询CarModel,了解到底是什么状态改变了,从而作出响应的反应。
层次(分层)系统是按层次组织的。每层都为其上面的层提供服务,并充当下面层的客户。
三层架构是近年来经常使用的软件体系结构。
该体系结构可以分为通用的3层架构与运行在互联网上的三层架构软件。
传统的三层的层次体系结构-与Internet无关
COBOL:非结构化编程语言代表C:结构化编程语言代表C++:面向对象语言,面向对象编程Java:100%面向对象编程语言
主程序和子程序体系结构与面向对象系统都是调用-返回体系结构的子类型。
调用和返回体系结构的控制流
是一种Callandreturn架构,代表结构化编程
药品成本计算问题药品成本计算程序输入药品名称,程序将返回给定药品的单位数量成本药品费用包括两部分——Researchcost,andProductioncost
结构化设计的优点自顶向下的方法是非常成功的设计方法对于10万行以下的代码的程序设计没有问题
结构化设计的缺点当程序超过10万行代码的时候,该方法表现很差
原因代码开发变得越来越慢而且测试软件和保证其可靠性变得越来越困难
自顶向下功能化设计产生的的问题功能化设计不容易进化(可扩展性差)实际的系统很难按照功能化的观点刻画功能化丢失了数据功能化设计的程序复用性比较差
是一种Callandreturn架构
系统设计:系统可以看作是由一群对象组成
面向对象设计的原理:基于信息隐藏
面向对象设计的性质
面向对象设计的其它性质
面向对象设计的优点
a)程序容易维护(Easiermaintenance)由于对象对其客户程序隐藏其表示,因此可以在不影响客户程序的情况下改变实现。对象可以理解为独立实体;
b)对象是可复用的组件(Reusable);
c)在一些系统中,对象是现实现实世界的映射;
d)容易分解系统(Easydecompositionofasystem)对象将一组可访问的方法与其操作的数据捆绑在一起,允许设计人员将问题分解为互相交互的对象的集合。
结构化设计与面向对象设计的区别
项目描述
要求设计一个关键词查找程序1974年,Parnas提出了以下的问题:上下文中的关键词(KWIC)索引系统问题描述
程序接收一组有序的行;每一行都是一组有序的单词;每个单词都是一组有序的字符;通过反复删除每行的第一个单词并将其附加到行的末尾,每个行都会发生循环移位最后,将所有的已经移位的完成的行按照每行的首字母进行排序KWIC索引系统输出所有的行算法描述
1)输入一些行InputThesunisrisingintheeastFlowersareblooming2)进行圆圈移位(产生多行)AftercircularshiftThesunisrisingintheeastsunisrisingintheeasttheisrisingintheeastthesunrisingintheeastthesunisintheeastthesunisrisingtheeastthesunisrisingineastthesunisrisingintheFlowersarebloomingarebloomingFlowersbloomingFlowersare3)进行按照字母排序AfteralphabetizerarebloomingFlowersbloomingFlowersareeastthesunisrisingintheflowersarebloomingintheeastthesunisrisingisrisingintheeastthesunrisingintheeastthesunissunisrisingintheeastthetheeastthesunisrisinginThesunisrisingintheeast(a)案例一用主程序-子程序架构设计KWIC系统
主/子例程架构的解决方案,用共享数据分解系统。根据所执行的四种基本功能:
input,circularlyShift,sortandoutput.这些函数被协调成子程序由一个主程序来完成
用面向对象系统设计KWIC系统
将系统分解为一些模块将系统分解成模块
将模块组合成图模块组装成一个图形
运行该图运行图表
1960年代-1970年代数据处理的例子
批顺序处理是由一系列处理步骤组成的,中间是某种存储设备,如磁带、硬盘等。
每一步都将对输入存储执行一些操作,以获得一些有用的信息或修改源存储的内容,然后将结果数据保存到其接收器存储中。
在一个文本文件中,将所有年份表达式“xx”改为“xxxx”,例如“89”改为“1989”,将所有年份表达式“RepublicChina”改为“Taiwan”。
基本知识:一幅图片使用一个216X216;或者512X512;或者其它的矩阵表示。矩阵中的元素是0~255的正整数,叫做灰度值,0代表纯粹的黑色,255代表白色;其余介于0与255之间的数值,代表灰色。每一个元素具有一个特定的位置(x,y)和幅值f(x,y),这些元素就称为像素。图像处理是按照一定的算法,对图像像素的灰度值进行变换。
设计考虑设计一个图像处理软件。该软件包含一些可以随时添加的过滤器(filters),例如
Blurring(图像模糊),Sharpening(图像锐化)Brightening(图像变亮)EdgeDetector(发现图像边界),一些过滤器被串联在一起,以便完成一些比较复杂的功能。
项目需求:设计与实现一个人口信息处理程序。
将一个大的人口数据文件按照性别、年龄分为多个文件使用批处理体系结构进行设计,如下图所示。
管道和过滤器架构由进行数据处理的过滤器和将数据从一个过滤器传送到下一个过滤器的管道组成。
此架构适合流数据的处理,例如宇宙飞船传回的数据。
相似之处:处理过程之间互不调用(independentprocessingmodules)
区别
问题描述:利用管道-过滤器体系结构设计一个旧文件更新系统
修复两千年问题:查找并更改所有的年份表达式,如“xy”为“19xy”,“89”为“1989”修复政治问题:查找并更改所有“中华民国”为“台湾”
设计:使用Pipe-and-filter软件体系结构
设计要求1:怎样使4个过滤器对象同时工作
解决方案:利用多线程机制:让Filter类实现JavaAPI的接口类Runnable
方法
Start():启动一个线程并运行线程Run():在需要时由线程调用Stop():停止一个线程Transform():抽象方法 输入数据(输入过滤器), 查找并处理千年虫问题(使用千年虫过滤器), 发现和修复政治问题(在政治过滤器) 输出数据运行过程:start=>run=>transform()
考虑满足设计要求2:数据像水流一样,从左至右流经各个管道与过滤器。
回答设计要求2:怎样使得数据像水流一样从左向右移动?
最后设计类图,可以作为一个模板使用
什么是过程?
基于事件的系统:事件广播、事件注册、方法被自动调用
隐式调用风格中的组件是一个模块,其接口同时提供一个过程集合(C++中的函数、java中的方法)一个事件的集合
考虑一个C、c++或Java的集成开发环境。这样的IDE由诸如编辑器、源代码编辑器、变量监控器、变量监控、调试器、一个调试器等等。这样的系统通常使用基于事件的体系结构。
编辑器和变量监视器注册调试器的断点事件。
当调试器运行到断点处而停止时,它会宣布一个事件,允许系统自动调用那些已注册的工具的过程。
这些过程可以——①将编辑器滚动到源码的适当的行②显示监视变量的值。
注意:①调试器仅仅广播了一个事件,但是不知道其它的组件将要做什么。②松耦合。
既然在事件系统中,当一个事件被广播了,系统将自动调用那些已经注册了的组件,那么就有了以下的问题。问题:怎样将事件发送到已经注册了的组件中呢
这个问题要分两种情况考虑策略1:有独立事件调度模块的系统。策略2:无中心事件调度模块的系统。
事件调度模块的责任(1)接收事件(2)分发事件
而调度模块以两种方式分发事件(1)广播事件给系统的全部模块调度程序可以向系统中的所有模块广播事件(2)仅发送事件给那些已经注册了该事件的模块调度员只是发送一个事件,这些模块,注册事件:发布/订阅策略(发布/订阅策略)
方式1:调度模块向全部模块广播事件调度程序可以向系统中的所有模块广播事件
方式2:调度模块只向那些注册了该事件的模块发送事件
这个模型通常被称为observable/Observer每个模块都允许其它模块对其所发送的事件感兴趣只将事件发送给注册者
设计一个机场信息显示系统
机场信息显示系统的责任:接收将要飞离机场与到达机场的航班信息并将这些信息通知给旅客,包括所有到港飞机和离港飞机的准时、延误等信息。
初步设计三个类
InforCenter类:收集所有的航班信息VoiceInfo类:广播所有的航班信息给所有的旅客WordInfo类:以文字的形式将所有的航班信息显示在屏幕上
可理解该系统为一个事件系统(航班准时,延迟是事件)利用观察者模式设计的机场信息发布系统
观察者模式可以被用来设计与实现比较简单的事件系统观察者模式可以被认为是Observable/Observer模型(无事件空间;事件空间由Observables产生)