版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
软件设计与设计模式
—Strategy模式软件设计本讲内容一、软件设计回顾二、一个具体的设计问题三、设计模式的概念2本讲内容一、软件设计回顾二、一个具体的设计问题三、设计模式的概念3软件设计软件设计主要针对需求分析得到的软件需求规格说明,综合考虑各种制约因素,探求切实可行的软件解决方案并最终给出方案的逻辑表示,包括文档、模型等。制约因素:资源制约技术制约为什么不直接将需求模型映射到编码?4软件设计模型设计元素:指出现在设计模型中的模块,包括子系统、构件、类、过程等。设计模型:软件问题的解决方案的逻辑表示设计模型应该宏观地说明目标软件系统的整体结构(体系结构)——体系结构设计用户界面的设计方案——界面设计设计元素如何协同工作以实现每项软件需求——用例设计子系统/构件/类/过程的内部元素的属性、算法,以及这些内部元素之间的协作关系——模块设计持久数据的设计方案——数据模型设计5软件设计的基本原则6抽象与逐步求精模块化信息隐藏关注点分离1、抽象与逐步求精抽象:心理学概念,要求人们将注意力集中在某一层次上考虑问题,而忽略那些低层次的细节。逐步求精:自顶向下设计策略主要思想是,针对某个功能的宏观描述用逐步求精的方法不断地分解,逐步确立过程细节,直至该功能用程序语言描述的算法实现为止。在软件设计过程中,抽象与逐步求精是一般都是结合起来进行应用。7示例82、模块化模块化思想:把软件划分为若干相对独立的部件,每个部件称为一个模块当把所有模块组装到一起时则获得满足问题需要的一个解。“分治法”将一个复杂问题分解成若干个可管理的小问题后更易于求解。9模块数目与成本10内聚度与耦合度什么是内聚度?指该模块内各成分间彼此结合的紧密程度什么是耦合度?模块间的相关程度,越低越好11设计一定要追求:强内聚、松耦合3、信息隐藏模块应该设计为使其所含信息(过程和数据)对于那些不需要这些信息的模块不可访问,模块之间仅交换那些为完成系统功能必须交换的信息。有助于强化模块独立性,消除模块之间不必要的依赖关系,从而支持强内聚、松耦合原则的实现。124、关注点分离关注点:问题求解者针对概念、任务或目标的某个部分或侧面的聚焦。例如:功能安全控制数据访问……如何分离关注点通过职责划分来分离关注点利用软件系统中各部分的通用性、稳定性/易变性的不同进行关注点分离采用支持关注点分离的软件建模和编程方法,如Aspect-OrientedSoftwareDevelopment13软件设计的一般过程14设计质量的重要性软件设计的质量不但对最终软件产品的质量起着决定性作用,还对软件开发过程以及软件日后在使用过程中维护的难易程度有着重要的影响。高质量的软件设计,能够有效缩短软件开发时间,减少开发成本,提高最终软件产品质量。15软件设计的质量要素结构良好充分性可行性简单性实用性灵活性健壮性可移植性可复用性标准化16如何提高软件设计的质量?17本讲内容一、软件设计回顾二、一个具体的设计问题三、设计模式的概念18开发一个模拟鸭子游戏SimUDuckJoe上班的公司开发了SimUDuck游戏,其中会出现各种鸭子,可以一边游泳嬉水,一边呱呱叫绿头鸭、红头鸭……19如何对各种鸭子的类进行设计?其他类型的鸭子参考书:Freeman.E等,《HeadFirstDesignPattern》SimUDuck的需求变化公司高管认为需要进一步提高游戏的效果和受欢迎度程序里增加会飞的鸭子,并交给Joe完成Joe认为很简单20如何做?其他类型的鸭子,例如橡皮鸭对代码做局部的修改,影响层面可不只是局部。新的设计模型橡皮鸭不会呱呱叫,会吱吱叫,不会飞21如何修改设计?覆盖为吱吱叫覆盖,什么事都不做进一步扩展增加诱饵鸭,是一种木头鸭,即不会叫,也不会飞22如何修改设计?覆盖,什么事都不做覆盖,什么事都不做问题在哪?当涉及维护时,为了复用目的而使用继承,结果并不完美23利用继承来提供Duck的行为,会导致下面哪些缺点?代码在多个子类中重复运行时的行为不容易改变不能让鸭子跳舞很难知道所有鸭子的全部行为鸭子不能同时又飞又叫改变会牵一发而动全身,造成其他鸭子不想要的改变主管通知:以后需要经常更新产品,出现新的鸭子类型。如何解决问题?新方案:采用接口把fly()和quack()从超类中拿出,设计两个接口Flyable和Quackable,每种鸭子各自实现所需的接口。24如何修改设计?你觉得这个设计如何?主要问题?接口和抽象类有何区别?软件开发一个不变的真理有哪些驱动软件发生改变的因素?25CHANGE需要用一种对既有代码影响最小的方式来修改软件,可以花较少时间进行代码重写,还可以减少发生错误的可能。重新考虑SimUDuck面临的问题应对发生的代码变化,采用什么原则进行设计?26设计原则一找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。把会变化的部分取出并进行封装,以后可以轻易的改动或扩充此部分,而不影响不需要变化的部分。效果:代码变化引起的不经意后果变少,系统变得更有弹性如何处理Duck类?取出Duck中的易变部分Duck中哪些代码容易变化方法fly()和quack(),把它们从Duck类中取出建立新的类如何定义各种不同的飞行行为和叫声?采用接口并进行不同实现27设计原则二针对接口编程,而不是针对实现编程。如何动态改变Duck行为在Duck类中包含行为设定的方法,以运行时动态改变行为目前的接口设计28实现鸭子飞行什么都不做实现呱呱叫实现吱吱叫什么都不做问题1:加入一个火箭动力的飞行动作,如何做?问题2:可能还有什么类用到呱呱叫?在Duck中整合鸭子行为通过加入FlyBehavior和QuackBehavior类型的实例变量声明为接口类型,每个具体鸭子对象动态实例化正确的行为类型29publicclassDuck{FlyBehaviorflyBehavior;QuackBehaviorquackBehavior;publicvoidperformFly(){flyBehavior.fly();}publicvoidperformQuack(){quackBehavior.quack();}}Duck类中实现动作的执行不在乎具体行为是什么,只关心如何执行行为设定具体鸭子类的行为30publicclassMallardDuckextendsDuck{publicMallardDuck(){quackBehavior=newQuack();flyBehavior=newFlyWithWings();}publicvoiddisplay(){System.out.println("I'marealMallardduck");}}问题:这个类如何实现呱呱叫和用翅膀飞行?为什么可以这样做?对设计进行实现31publicabstractclassDuck{FlyBehaviorflyBehavior;QuackBehaviorquackBehavior;publicDuck(){}abstractvoiddisplay();publicvoidperformFly(){flyBehavior.fly();}publicvoidperformQuack(){quackBehavior.quack();}publicvoidswim(){System.out.println("Allducksfloat,evendecoys!");}}Duck.javapublicinterfaceFlyBehavior{publicvoidfly();}Flybehavior.javapublicclassFlyWithWingsimplementsFlyBehavior{publicvoidfly(){System.out.println("I'mflying!!");}}FlyWithWings.javapublicclassFlyNoWayimplementsFlyBehavior{publicvoidfly(){System.out.println("Ican'tfly");}}FlyNoWay.java对设计进行实现32publicinterfaceQuackBehavior{publicvoidquack();}QuackBehavior.javapublicclassQuackimplementsQuackBehavior{publicvoidquack(){System.out.println("Quack");}}Quack.javapublicclassMuteQuackimplementsQuackBehavior{publicvoidquack(){System.out.println("<<Silence>>");}}MuteQuack.javapublicclassSqueakimplementsQuackBehavior{publicvoidquack(){System.out.println("Squeak");}}Squeak.javapublicclassMallardDuckextendsDuck{publicMallardDuck(){quackBehavior=newQuack();flyBehavior=newFlyWithWings();}publicvoiddisplay(){System.out.println("I'marealMallardduck");}}MallardDuck.javapublicclassMiniDuckSimulator1{
publicstaticvoidmain(String[]args){Duckmallard=newMallardDuck();mallard.performQuack();mallard.performFly();}}MiniDuckSimulator1.java运行结果是什么?如何在运行中动态设定行为?33publicvoidsetFlyBehavior(FlyBehaviorfb){flyBehavior=fb;}publicvoidsetQuackBehavior(QuackBehaviorqb){quackBehavior=qb;}Duck类中加入两个新方法问题:现在我们要增加一个模型鸭(ModelDuck)类型,一开始不会飞但会呱呱叫,后来建立一个利用火箭动力进行飞行的行为并赋予模型鸭。如何实现?新增加的代码34publicclassModelDuckextendsDuck{publicModelDuck(){flyBehavior=newFlyNoWay();quackBehavior=newQuack();}publicvoiddisplay(){System.out.println("I'mamodelduck");}}ModelDuck.javapublicclassFlyRocketPoweredimplementsFlyBehavior{publicvoidfly(){System.out.println("I'mflyingwitharocket");}}FlyRocketPowered.javapublicclassMiniDuckSimulator1{
publicstaticvoidmain(String[]args){
Duckmallard=newMallardDuck();mallard.performQuack();mallard.performFly();
Duckmodel=newModelDuck();model.performFly();model.setFlyBehavior(newFlyRocketPowered());model.performFly();}}MiniDuckSimulator1.java运行结果是什么?为什么?请看代码MiniDuckSimulator.java的运行结果是什么?35SimUDuck整体类图?36飞行行为发声行为客户可以看作算法族算法可以相互替换设计原则三多用组合,少用继承。Strategy模式:定义了算法族,分别封装起来,让他们之间可以相互替换,让算法的变化独立于使用算法的客户。Strategy模式结构37设计原则一找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。设计原则二针对接口编程,而不是针对实现编程。设计原则三多用组合,少用继承。练习38本讲内容一、软件设计回顾二、一个具体的设计问题三、设计模式的概念39软件设计面临的问题从需求到设计没有按部就班的步骤OOA用直观的方式给出问题描述剩下就靠经验40OOAArchitectureOODOOPUML?magic?什么是模式?来自于建筑学41“Eachpatterndescribesaproblemwhichoccursoverandoveragaininourenvironment,thendescribesthecoreofthesolutiontothatproblem,insuchawaythatyoucanusethissolutionamilliontimesover,withouteverdoingitthesamewaytwice”
“Anideathathasbeenusefulinonepracticalcontextandwillprobablybeusefulinothers”软件设计模式42在面向对象软件的设计中,采用对象建模技术表示针对某些问题的通用解决方案,这些方案来自于众多实际系统的开发经验。《DesignPatterns:ElementsofReusableObject-OrientedSoftware(1995)》ErichGammaRichardHelmRalphJohnsonJohnVlissides(a.k.a.TheGangofFour)设计模式的特征精巧对于新手不易发现的、巧妙地解决方案通用不依赖于具体系统、编程语言或问题域经过证明已经在真实系统中经过应用证实简单规模不大,设计中包含的类数量不多可复用具有文档描述,容易被学习和重复使用面向对象使用面向对象的机制,例如类、对象、泛化、多态等43设计模式包含的基本内容模式名称能直观体现模式的含义问题描述用于解决什么问题及其所处的上下文环境解决方案解决问题要使用哪些元素、以及元素间关系效果使用该模式的结果以及相关因素的权衡44GoF设计模式45
目标创建型结构型行为型范围类工厂方法(FactoryMethod)适配器(Adapter)解释器(Interpreter)模板方法(TemplateMethod)对象抽象工厂(AbstractFactory)构建者(Builder)原型(Prototype)单件(Singleton)适配器(Adapter)桥接(Bridge)组合(Composite)装饰器(Decorator)外观(Facade)享元(Flyweight)代理(Proxy)职责链(ChainofResponsibility)命令(Command)迭代器(Iterator)中介者(Mediator)备忘录(Memento)观察者(Observer)状态(State)策略(Strategy)访问者(Visitor)设计模式的分类根据目标分类创建型:关注对象的创建过程结构型:处理类和对象的组合结构行为型:给出类或对象交互的方式和职责分配根据范围分类类范围:关注类、子类之间的关系,侧重静态对象范围:关注对象之间的关系,更加侧重动态和运行时的改变46如何使用设计模式当面临一个设计问题时,优秀的设计人员首先查找是否有可用模式能解决该问题或接近解决问题使用步骤步骤1:理解已有设计模式步骤2:使用模式直接复用,针对对特定问题进行改造(总是需要的)或作为形成新的更适用、更佳方案的灵感来源47小结知道OO基础不足以设计出良好的OO系统良好的OO设计要具备可复用、可扩展、可维护性模式被认为是经过历史验证的OO设计经验模式不是代码,是针对设计问题的通用解决方案大多数模式和原则,都着眼于软件变化的主题大多数的模式都允许系统局部改变独立于其他部分把系统中会变化的部分抽出来封装模式让开发人员之间有共享的语言48
软件设计模式:Observer软件设计建立一个气象监测系统为某个气象站开发一个气象监测软件跟踪天气状况温度湿度气压布告板目前天气状况历史气象统计未来天气预报布告板根据最新测量数据实时更新良好的可扩展性公开API,其他开发人员可以建立自己的气象布告板50气象监测系统的框架51湿度感应装置温度感应装置气压感应装置气象站WeatherData对象显示取数据要开发的软件系统如何设计WeatherData类52/**一旦气象测量更新,此方法会被调用*/publicvoidmeasurementsChanged(){//你的代码加在这里}WeatherData.java获取温度、湿度和气压测量值当前天气天气预报历史统计?如何实现?53publicclassWeatherData{……publicvoidmeasurementsChanged(){floattemp=getTemperature();floathumidity=getHumidity();floatpressure=getPressure();currentConditionsDisplay.update(temp,humidity,pressure);statisticsDisplay.update(temp,humidity,pressure);forecastDisplay.update(temp,humidity,pressure);}……}这是针对具体实现编程,而非针对接口。对于每个新的布告板,都得修改代码。无法在运行时动态地增加或删除布告板。布告板没有实现一个共同的接口。尚未封装改变的部分。侵犯了WeatherData类的封装。哪个说法正确?改变的地方需要封装起来生活中类似的情景?报纸订阅报社出版报纸(更新信息)读者向出版社订阅每次有新报纸就会送给订户订户不想再订就取消订阅不断有人订阅或取消订阅54出版者+订阅者=观察者模式SubjectSubscriberObserver定义了对象之间的一对多依赖当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新如何设计Observer模式的类图55观察者模式提供了一种让对象之间松耦合交互的方式主题维护一个观察者对象的列表,可以随时增删观察者发生变化时,不需要修改主题代码可以独立的复用主题和观察者设计原则四为了交互对象之间的松耦合设计而努力。请画出气象站的类图56如何增加一个新的布告板?代码实现—接口57publicinterfaceSubject{publicvoidregisterObserver(Observero);publicvoidremoveObserver(Observero);publicvoidnotifyObservers();}Subject.javapublicinterfaceObserver{publicvoidupdate(floattemp,floathumidity,floatpressure);}Observer.javapublicinterfaceDisplayElement{publicvoiddisplay();}DisplayElement.java如何把观测值送给观测者?有什么问题吗?代码实现—WeatherData58importjava.util.*;publicclassWeatherDataimplementsSubject{privateArrayList<Observer>observers;privatefloattemperature;privatefloathumidity;privatefloatpressure;publicWeatherData(){observers=newArrayList<Observer>();}publicvoidregisterObserver(Observero){observers.add(o);}publicvoidremoveObserver(Observero){inti=observers.indexOf(o);if(i>=0){observers.remove(i);}}WeatherData.java代码实现—WeatherData(续)59publicvoidnotifyObservers(){for(Observerobserver:observers){observer.update(temperature,humidity,pressure);}}publicvoidmeasurementsChanged(){notifyObservers();}publicvoidsetMeasurements(floattemperature,floathumidity,floatpressure){this.temperature=temperature;this.humidity=humidity;this.pressure=pressure;measurementsChanged();}}代码实现—布告板160publicclassCurrentConditionsDisplayimplementsObserver,DisplayElement{privatefloattemperature;privatefloathumidity;privateSubjectweatherData;publicCurrentConditionsDisplay(SubjectweatherData){this.weatherData=weatherData;weatherData.registerObserver(this);}publicvoidupdate(floattemperature,floathumidity,floatpressure){this.temperature=temperature;this.humidity=humidity;display();}publicvoiddisplay(){System.out.println("Currentconditions:"+temperature+"Fdegreesand"+humidity+"%humidity");}}是否需要保存该引用?CurrentConditionsDisplay.java代码实现—布告板261publicclassForecastDisplayimplementsObserver,DisplayElement{privatefloatcurrentPressure=29.92f;privatefloatlastPressure;privateWeatherDataweatherData;publicForecastDisplay(WeatherDataweatherData){this.weatherData=weatherData;weatherData.registerObserver(this);}publicvoidupdate(floattemp,floathumidity,floatpressure){lastPressure=currentPressure;currentPressure=pressure;display();}publicvoiddisplay(){System.out.print("Forecast:");if(currentPressure>lastPressure){System.out.println("Improvingweatherontheway!");}elseif(currentPressure==lastPressure){System.out.println("Moreofthesame");}elseif(currentPressure<lastPressure){System.out.println("Watchoutforcooler,rainyweather");}}}ForecastDisplay.java代码实现—布告板362publicclassStatisticsDisplayimplementsObserver,DisplayElement{privatefloatmaxTemp=0.0f;privatefloatminTemp=200;privatefloattempSum=0.0f;privateintnumReadings;privateWeatherDataweatherData;publicStatisticsDisplay(WeatherDataweatherData){this.weatherData=weatherData;weatherData.registerObserver(this);}publicvoidupdate(floattemp,floathumidity,floatpressure){tempSum+=temp;numReadings++;if(temp>maxTemp){maxTemp=temp;}if(temp<minTemp){minTemp=temp;}display();}publicvoiddisplay(){System.out.println("Avg/Max/Mintemperature="+(tempSum/numReadings)+"/"+maxTemp+"/"+minTemp);}}StatisticsDisplay.java代码实现—气象站测试程序63publicclassWeatherStation{publicstaticvoidmain(String[]args){WeatherDataweatherData=newWeatherData();CurrentConditionsDisplaycurrentDisplay=newCurrentConditionsDisplay(weatherData);StatisticsDisplaystatisticsDisplay=newStatisticsDisplay(weatherData);ForecastDisplayforecastDisplay=newForecastDisplay(weatherData);weatherData.setMeasurements(80,65,30.4f);weatherData.setMeasurements(82,70,29.2f);weatherData.setMeasurements(78,90,29.2f);}}WeatherStation.java运行结果64增加一个酷热指数布告板如何计算酷热指数t:温度,rh:湿度65如何对程序进行改造?“推(push)”和“拉(pull)”66publicvoidnotifyObservers(){for(Observerobserver:observers){observer.update(temperature,humidity,pressure);}}publicfloatgetTemperature(){returntemperature;}publicfloatgetHumidity(){returnhumidity;}publicfloatgetPressure(){returnpressure;}Java内置的观察者模式java.util包内包含Observer接口和Observable类67通知发送与接收机制可观察者如何送出通知调用setChanged()方法,标记状态已改变调用两种通知方法之一notifyObservers()notifyObservers(Objectarg)观察者如何接收通知update(Observableo,Objectarg)如何“推”?如何“拉”?68拉推setChanged()方法69setChanged(){changed=true}notifyObservers(Objectarg){if(changed){foreveryobserveronthelist{callupdate(this,arg)}changed=false}}notifyObservers(){notifyObservers(null)}可以更灵活设置什么条件下才会更新,例如变化超过0.1度还是0.5度新的代码70importjava.util.Observable;publicclassWeatherDataextendsObservable{privatefloattemperature;privatefloathumidity;privatefloatpressure;publicWeatherData(){}publicvoidmeasurementsChanged(){setChanged();notifyObservers();}publicvoidsetMeasurements(floattemperature,floathumidity,floatpressure){this.temperature=temperature;this.humidity=humidity;this.pressure=pressure;measurementsChanged();}publicfloatgetTemperature(){returntemperature;}publicfloatgetHumidity(){returnhumidity;}publicfloatgetPressure(){returnpressure;}}WeatherData.java新的代码71CurrentConditionsDisplay.javaimportjava.util.Observable;importjava.util.Observer; publicclassCurrentConditionsDisplayimplementsObserver,DisplayElement{ Observableobservable; privatefloattemperature; privatefloathumidity; publicCurrentConditionsDisplay(Observableobservable){ this.observable=observable; observable.addObserver(this); } publicvoidupdate(Observableobs,Objectarg){ if(obsinstanceofWeatherData){ WeatherDataweatherData=(WeatherData)obs; this.temperature=weatherData.getTemperature(); this.humidity=weatherData.getHumidity(); display(); } } publicvoiddisplay(){ System.out.println("Currentconditions:"+temperature +"Fdegreesand"+humidity+"%humidity"); }}新的代码(请排序)72publicvoiddisplay(){System.out.print("Forecast:");if(currentPressure>lastPressure){System.out.println("Improvingweatherontheway!");}elseif(currentPressure==lastPressure){System.out.println("Moreofthesame");}elseif(currentPressure<lastPressure){System.out.println("Watchoutforcooler,rainyweather");}}}ForecastDisplay.javaimportjava.util.Observable;importjava.util.Observer;publicclassForecastDisplayimplementsObserver,DisplayElement{privatefloatcurrentPressure=29.92f;privatefloatlastPressure;publicForecastDisplay(Observableobservable){observable.addObserver(this);}publicvoidupdate(Observableobservable,Objectarg){if(observableinstanceofWeatherData){WeatherDataweatherData=(WeatherData)observable;lastPressure=currentPressure;currentPressure=weatherData.getPressure();display();}}ABCDEFGHIJKDIKHBJGFECA使用java.util.Observable的问题Observable是一个类,难以多重继承setChanged()的可见性是保护型,只能继承使用难以组合使用(违反哪个原则?)73JDK中其他采用观察者模式的机制SwingAPI:JButton增加与删除监听者(listener)74请阅读程序SwingObserverExample.java目前的设计原则75设计原则一找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。设计原则二针对接口编程,而不是针对实现编程。设计原则三多用组合,少用继承。设计原则四为了交互对象之间的松耦合设计而努力。观察者模式如何遵循前三个设计准则?小结观察者模式定义了对象之间的一对多关系主题用一个共同的接口来更新观察者主题和观察者之间用松耦合方式结合可以“推”或“拉”数据Java中有多种观察者模式的实现76
软件设计模式:
Factorymethod&Abstractfactory软件设计问题提出如何将实例化具体类的代码从应用中抽离或者封装起来,使他们不会干扰到应用的其他部分?78Duckduck;if(picnic){duck=newMallardDuck();}elseif(hunting){duck=newDecoyDuck();}elseif(inBathTub){duck=newRubberDuck();}设计原则一找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。设计原则五类应该对扩展开放,对修改关闭。案例:一个比萨店79publicclassPizzaStore{PizzaorderPizza(){Pizzapizza=newPizza();pizza.prepare();pizza.bake();pizza.cut();pizza.box();returnpizza;}}如何根据顾客的需要制作不同类型的比萨?publicclassPizzaStore{PizzaorderPizza(Stringtype){Pizzapizza;if(type.equals("cheese")){pizza=newCheesePizza();}elseif(type.equals(“greek")){pizza=newGreekPizza();}elseif(type.equals(“pepperoni")){pizza=newPepperoniPizza();}pizza.prepare();pizza.bake();pizza.cut();pizza.box();returnpizza;}}参考书:Freeman.E等,《HeadFirstDesignPattern》但顾客口味发生了变化80最近流行ClamPizza和VeggiePizza,而GreekPizza卖的不好,如何变化?publicclassPizzaStore{PizzaorderPizza(Stringtype){Pizzapizza;if(type.equals("cheese")){pizza=newCheesePizza();}elseif(type.equals(“greek")){pizza=newGreekPizza();}elseif(type.equals(“pepperoni")){pizza=newPepperoniPizza();}elseif(type.equals(“clam")){pizza=newClamPizza();}elseif(type.equals(“veggie")){pizza=newPepperoniPizza();}pizza.prepare();pizza.bake();pizza.cut();pizza.box();returnpizza;}}这是不变的地方如何将变化部分从不变中抽离?把创建Pizza的代码移到另一个对象中称为“工厂”81publicclassPizzaStore{PizzaorderPizza(Stringtype){Pizzapizza;pizza.prepare();pizza.bake();pizza.cut();pizza.box();returnpizza;}}publicclassSimplePizzaFactory{publicPizzacreatePizza(Stringtype){Pizzapizza=null;if(type.equals("cheese")){pizza=newCheesePizza();}elseif(type.equals(“pepperoni")){pizza=newPepperoniPizza();}elseif(type.equals(“clam")){pizza=newClamPizza();}elseif(type.equals(“veggie")){pizza=newPepperoniPizza();}returnpizza;}}只是把变化换了个地方,问题依然存在,这样有什么好处?重构PizzaStore类82publicclassPizzaStore{SimplePizzaFactoryfactory;
publicPizzaStore(SimplePizzaFactoryfactory){this.factory=factory;}PizzaorderPizza(Stringtype){Pizzapizza;pizza=factory.createPizza(type);pizza.prepare();pizza.bake();pizza.cut();pizza.box();returnpizza;}}如何在运行时更换不同的实现?如何创建纽约、芝加哥、加州等不同风味的Pizza?简单工厂一种编程习惯,不被当作真正的模式83工厂的客户创建Pizza的工厂工厂的产品工厂的具体产品进一步扩展建立了多家比萨加盟店,各自提供不同风味的比萨,如纽约、芝加哥、加州三种风味。如何扩展?84增加三个工厂NYPizzaFactorynyFactory=newNYPizzaFactory();PizzaStorenyStore=newPizzaStore(nyFactory);nyStore.orderPizza(“veggie“);ChicagoPizzaFactorychicagoFactory=newChicagoPizzaFactory();PizzaStorechicagoStore=newPizzaStore(chicagoFactory);chicagoStore.orderPizza(“veggie“);对于加州(California),如何写这段代码?更改比萨做法不同加盟店同一种口味的比萨可能有不同做法和原料,例如纽约风味cheese比萨,每个比萨店希望自己决定并能控制质量。如何修改?对订单的处理流程都是一致的。85publicabstractclassPizzaStore{
publicPizzaorderPizza(Stringtype){Pizzapizza;pizza=createPizza(type);pizza.prepare();pizza.bake();pizza.cut();pizza.box();returnpizza;}protectedabstractPizzacreatePizza(Stringtype);}把工厂对象移到这个抽象方法中(工厂方法)每个比萨店自己负责制作86publicPizzacreatePizza(Stringtype){if(type.equals("cheese")){pizza=newNYStyleCheesePizza();}elseif(type.equals(“pepperoni")){pizza=newNYStylePepperoniPizza();}elseif(type.equals(“clam")){pizza=newNYStyleClamPizza();}elseif(type.equals(“veggie")){pizza=newNYStylePepperoniPizza();}}???orderPizza方法对Pizza进行处理,但不知道具体哪个类参与进来,这是“解耦”具体的一家比萨店87publicclassNYPizzaStoreextendsPizzaStore{PizzacreatePizza(Stringitem){if(item.equals("cheese")){returnnewNYStyleCheesePizza();}elseif(item.equals(“pepperoni")){returnnewNYStylePepperoniPizza();}elseif(item.equals(“clam")){returnnewNYStyleClamPizza();}elseif(item.equals(“veggie")){returnnewNYStylePepperoniPizza();}elsereturnnull;}}完成芝加哥Chicago和加州California风味的比萨店工厂方法88publicabstractclassPizzaStore{
publicPizzaorderPizza(Stringtype){Pizzapizza;pizza=createPizza(type);pizza.prepare();pizza.bake();pizza.cut();pizza.box();returnpizza;}protectedabstractPizzacreatePizza(Stringtype);}abstractProductfactoryMethod(stringtype)依赖子类处理对象创建返回一个产品可能需要参数来指定产品类型实现Pizza89publicabstractclassPizza{Stringname;Stringdough;Stringsauce;ArrayList<String>toppings=newArrayList<String>();
voidprepare(){System.out.println("Prepare"+name);System.out.println("Tossingdough...");System.out.println("Addingsauce...");System.out.println("Addingtoppings:");for(Stringtopping:toppings){System.out.println(""+topping);}}voidbake(){System.out.println("Bakefor25minutesat350");}voidcut(){System.out.println("Cutthepizzaintodiagonalslices");}voidbox(){System.out.println("PlacepizzainofficialPizzaStorebox");}publicStringgetName(){returnname;}}实现具体Pizza90publicclassNYStyleCheesePizzaextendsPizza{publicNYStyleCheesePizza(){name="NYStyleSauceandCheesePizza";dough="ThinCrustDough";sauce="MarinaraSauce";
toppings.add("GratedReggianoCheese");}}publicclassChicagoStyleCheesePizzaextendsPizza{publicChicagoStyleCheesePizza(){name="ChicagoStyleDeepDishCheesePizza";dough="ExtraThickCrustDough";sauce="PlumTomatoSauce";
toppings.add("ShreddedMozzarellaCheese");}
voidcut(){System.out.println("Cuttingthepizzaintosquareslices");}}测试驱动91publicclassPizzaTestDrive{
publicstaticvoidmain(String[]args){PizzaStorenyStore=newNYPizzaStore();PizzaStorechicagoStore=newChicagoPizzaStore();
Pizzapizza=nyStore.orderPizza("cheese");System.out.println("Ethanordereda"+pizza.getName()+"\n");
pizza=chicagoStore.orderPizza("cheese");System.out.println("Joelordereda"+pizza.getName()+"\n");pizza=nyStore.orderPizza("clam");System.out.println("Ethanordereda"+pizza.getName()+"\n");
pizza=chicagoStore.orderPizza("clam");System.out.println("Joelordereda"+pizza.getName()+"\n");}}比萨店的类图92比萨店的类图93如何增加加州风味的比萨店?Factorymethod模式工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,即让类把实例化推迟到子类。94不使用工厂模式的版本95publicclassDependentPizzaStore{
publicPizzacreatePizza(Stringstyle,Stringtype){Pizzapizza=null;if(style.equals("NY")){if(type.equals("cheese")){pizza=newNYStyleCheesePizza();}elseif(type.equals("veggie")){pizza=newNYStyleVeggiePizza();}elseif(type.equals("clam")){pizza=newNYStyleClamPizza();}elseif(type.equals("pepperoni")){pizza=newNYStylePepperoniPizza();}}elseif(style.equals("Chicago")){if(type.equals("cheese")){pizza=newChicagoStyleCheesePizza();}elseif(type.equals("veggie")){pizza=newChicagoStyleVeggiePizza();}elseif(type.equals("clam")){pizza=newChicagoStyleClamPizza();}elseif(type.equals("pepperoni")){pizza=newChicagoStylePepperoniPizza();}}else{System.out.println("Error:invalidtypeofpizza");returnnull;}pizza.prepare();pizza.bake();pizza.cut();pizza.box();returnpizza;}}如何增加加州风味的比萨店?设计原则六要依赖抽象,不要依赖具体类。依赖倒置96PizzaStoreNYStyleCheesePizzaPizzaStorePizzaNYStyleCheesePizza指导方针变量不要持有具体类的引用不要让类派生自具体类不要覆盖基类中已实现的方法97尽量(不是绝对)遵循上述指导方针进一步完善比萨店不同风味的Pizza使用的原料不同使用的原料应该配套98纽约风味FreshClams(新鲜蛤蜊)MarinaraSauce(大蒜番茄酱)ThinCrustDough(薄面饼)ReggianoCheese(Reggiano奶酪)若干种类蔬菜Pepperoni(意式腊肠)芝加哥风味FrozenClams(冷冻蛤蜊)PlumTomatoSauce(梅子番茄酱)ThickCrustDough(厚面饼)MozzarellaCheese(Mozzarella奶酪)若干种类蔬菜Pepperoni(意式腊肠)如何增加?建立为各地区生产原料的工厂工厂的接口99publicinterfacePizzaIngredientFactory{
publicDoughcreateDough();publicSaucecreateSauce();publicCheesecreateCheese();publicVeggies[]createVeggies();publicPepperonicreatePepperoni();publicClamscreateClam();}纽约原料工厂100publicclassNYPizzaIngredientFactoryimplementsPizzaIngredientFactory{
publicDoughcreateDough(){returnnewThinCrustDough();}publicSaucecreateSauce(){returnnewMarinaraSauce();}publicCheesecreateCheese(){returnnewReggianoCheese();}publicVeggies[]createVeggies(){Veggiesveggies[]={newGarlic(),newOnion(),newMushroom(),newRedPepper()};returnveggies;}publicPepperonicreatePepperoni(){returnnewSlicedPepperoni();}publicClamscreateClam(){returnnewFreshClams();}}芝加哥原料工厂的代码?101publicclassChicagoPizzaIngredientFactoryimplementsPizzaIngredientFactory{publicDoughcreateDough(){returnnewThickCrustDough();}publicSaucecreateSauce(){returnnewPlumTomatoSauce();}publicCheesecreateCheese(){returnnewMozzarellaCheese();}publicVeggies[]createVeggies(){Veggiesveggies[]={newBlackOlives(),newSpinach(),newEggplant()};returnveggies;}publicPepperonicreatePepperoni(){returnnewSlicedPepperoni();}publicClamscreateClam(){returnnewFrozenClams();}}重写Pizza类102publicabstractclassPizza{Stringname;Doughdough;Saucesauce;Veggiesveggies[];Cheesecheese;Pepperonipepperoni;Clamsclam;abstractvoidprepare();voidbake(){System.out.println("Bakefor25minutesat350");}voidcut(){System.out.println("Cuttingthepizzaintodiagonalslices");}voidbox(){System.out.println("PlacepizzainofficialPizzaStorebox");}voidsetName(Stringname){=name;}StringgetName(){returnname;}publicStringtoString(){//这里打印比萨的组成}}具体风味的Pizza不同区域的同类Pizza做法一样,只是原料不同,例如Cheese比萨不需要设计两个类来处理不同区域风味的同类Pizza103publicclassCheesePizzaextendsPizza{PizzaIngredientFactoryingredientFactory;
publicCheesePizza(PizzaIngredientFactoryingredientFactory){this.ingredientFactory=ingredientFactory;}
voidprepare(){System.out.println("Preparing"+name);dough=ingredientFactory.createDough();sauce=ingredientFactory.createSauce();cheese=ingredientFactory.createCheese();}}Cheese比萨实现Pizza和区域原料的解耦如何实现ClamPizza?104publicclassClamPizzaextendsPizza{PizzaIngredientFactoryingredientFactory;
publicClamPizza(PizzaIngredientFactoryingredientFactory){this.ingredientFactory=ingredientFactory;}
voidprepare(){System.out.println("Preparing"+name);dough=ingredientFactory.createDough();sauce=ingredientFactory.createS
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年《营养学基础》高频考点题库及答案
- 软件开发服务协议解除函(4篇)
- 2026年客户信用评估体系优化计划通知7篇
- 商业中心开发工程延期情况说明函3篇范本
- 运营部门运营效率绩效考评表
- 电子行业供应链整合与物联网应用邀请函(5篇)范文
- 人力资源招聘与面试评估操作手册
- 2026年辽阳市宏伟区中小学教师招聘考试参考试题及答案详解
- 2026年洛阳市吉利区中小学教师招聘笔试备考试题及答案详解
- 2026年上海市黄浦区中小学教师招聘笔试参考题库及答案详解
- 圆机操作工作业指导书
- 科技局遴选公务员面试经典题及答案
- 初中化学第一单元测试题及答案
- JJG 667-2025液体容积式流量计检定规程
- T/CASTEM 1006-2022科技评估报告编制通用要求
- 2025年天津市十二区重点学校毕业班高考英语联考试卷(一)
- 《大学生心理健康》课件全套 侯瑞鹤 主题1-10 心理健康课:送给自己大学生活的礼物-生命与成长:踏上成为自己的英雄之旅
- CNAS-GL033-2018 建设领域典型检验检测设备计量溯源指南
- 《Python金融数据分析与挖掘(微课版)》全套教学课件
- 人工智能大模型
- 初二物理期末试卷带答案
评论
0/150
提交评论