您好,欢迎访问三七文档
進入新章節1有些人已经解决你的问题了。在本章,你将学习到为何(以及如何)利用其他开发人员的经验与智慧。他们遭遇过相同的问题,也顺利地解决过这些问题。本章结束前,我们会看到设计模式的使用与优点,看看某些关键的OO设计原则,并透过一个范例来了解模式如何运作。使用模式昀好的方式是:「把模式装进脑子中,然后在你的设计和已有的应用中,寻找何处可以使用这些模式。」以往是代码复用,现在是经验复用。欢迎来到设计模式1介绍设计模式我们已经搬到对象村,刚刚开始着手设计模式...这里每个人都在使用设计模式。很快我们就会透过设计模式挤身上流社会。2第一章先从简单的模拟鸭子应用做起Joe上班的公司做了一套相当成功的模拟鸭子游戏SimUDuck游戏中出现各种鸭子,一边游泳戏水,一边呱呱叫。此系统的内部设计使用了标准的OO技术,设计了一个鸭子超类(Superclass),并让各种鸭子继承自此超类。Duckquack()swim()display()//鸭子的其他方法display(){//外观是绿头}MallardDuckdisplay(){//外观是红头}RedheadDuck许多其他类型的鸭子继承自Duck类。每個子类型(subtype)負責实现自己的display()行為,以在屏幕上顯示出該鴨子的外观。所有的鸭子都会呱呱叫(Quack)也会游泳(Swim),所以由超类负责处理这部分的实现代码。去年,公司的竞争压力加剧。在一个星期长的高尔夫假期兼头脑风暴会议之后,公司主管认为该是创新的时候了,他们需要在「下周」股东会议上展示一些「真正」让人印象深刻的东西来振奋人心。因为每一种鸭子的外观都不同,所以display()方法是抽象的。模拟鸭子介紹設計模式目前位置3Joe我只需要在Duck类中加上fly()方法,然后所有鸭子都会继承fly()。这是我大显身手,展示OO才华的时候了。所有的子类都会继承fly()Joe加上的主管认为,此模拟程序需要会飞的鸭子,将竞争者抛在后头。当然,在这个时候,Joe的经理拍胸脯告诉主管们,Joe只需要一个星期就可以搞定,「毕竟,Joe是一个OO程序员...这有什么困难?」现在我们得让鸭子能飞其他的鸭子类型...Duckquack()swim()display()fly()//鸭子的其他方法....display(){//外观是绿头}MallardDuckdisplay(){//外观是红头}RedheadDuck我們想要的4第一章他体会到了一件事:当涉及「维护」时,为了「复用」(reuse)目的而使用继承,结局并不完美。好吧!我承认设计中有一点小疏失。但是,他们怎么不干脆把这当成一种「特色」,其实还挺有趣的呀..Joe,我正在股东会议上,刚刚看了一下展示,有一只「橡皮鸭子」在屏幕上飞来飞去,这是你开的玩笑吗?你可能要开始去逛逛Monster.com(编注:美国昀大的求职网站)了...Joe忽略了一件事:并非Duck所有的子类都会飞。当Joe在Duck超类中加上新的行为,这会使得某些子类也具有这个不恰当的行为。现在可好了!SimUDuck程序中有一个会飞的非动物。对代码所做的局部修改,影响层面可能不只局部(会飞的橡皮鸭)!怎么回事?quack()swim()display()fly()//鸭子的其他方法display(){//外观是绿头}MallardDuckdisplay(){//外观是红头}RedheadDuckquack(){//覆盖成吱吱叫}display(){//外观是橡皮鸭}RubberDuckDuck橡皮鸭子不会呱呱叫,所以把quack()的定义覆盖成「吱吱叫」(squeak)。在超类中加上fly(),就会导致所有的子类都具备fly(),连那些不该具备fly()的子类也无法免除。但是,可怕的问题发生了...事情出错了介紹設計模式目前位置5Joe想到继承我可以把橡皮鸭类中的fly()方法覆盖掉,就好像覆盖quack()的作法一样...quack(){//吱吱叫}display(){//橡皮鸭}fly(){//覆盖,变成什么事都不做}RubberDuckA.代码在多个子类中重复。B.运行时的行为不容易改变。C.我们不能让鸭子跳舞。D.难以得知所有鸭子的全部行为。E.鸭子不能同时又飞又叫。F.改变会牵一发动全身,造成其他鸭子不想要的改变。利用繼承提供鴨子行為,會導致下列哪些缺點?(多選)quack(){//覆盖,变成什么事都不做}display(){//诱饵鸭}fly(){//覆盖,变成什么事都不做}DecoyDuck这是继承层次中的另一个类。注意,诱饵鸭既不会飞也不会叫,可是橡皮鸭不会飞但会叫。可是,如果以后我加入诱饵鸭(DecoyDuck),又会如何?诱饵鸭是假鸭,不会飞也不会叫...6第一章我可以把fly()取出来,放进一个「Flyable接口」中。这么一来,只有会飞的鸭子才实现此接口。同样的方式,也可以用来设计一个「Quackable接口」,因为不是所有的鸭子都会叫。display()fly()quack()MallardDuckdisplay()fly()quack()RedheadDuckdisplay()quack()RubberDuckswim()display()//鸭子的其他方法...Duckdisplay()DecoyDuckfly()Flyablequack()Quackable利用接口如何?Joe认识到继承可能不是一个好的解决方法,因为他刚刚拿到来自主管的备忘录,希望以后每六个月更新产品(至于更新的方法,他们还没想到)。Joe知道规格会常常改变,每当有新的鸭子子类出现,他就要被迫检视并可能需要覆盖fly()和quark()...这简直是无穷尽的恶梦。所以,他需要一个更清晰的方法,让「某些」(而不是全部)鸭子类型可飞或可叫。你觉得这个设计如何?繼承並非答案介紹設計模式目前位置7这真是一个超笨的主意,你没发现这么一来重复的代码会变多吗?如果你认为覆盖几个方法就算是差劲,那么对于在48个Duck的子类都要稍微修改一下飞行的行为,你又怎么说?!如果你是Joe,你要怎么办?我们知道,并非「所有」的子类都具有飞行和呱呱叫的行为,所以继承并不是适当的解决方式。虽然Flyable与Quackable可以解决「一部分」的问题(不会再有会飞的橡皮鸭),但是却造成代码无法复用,这只能算是从一个恶梦跳进另一个恶梦。甚至,在会飞的鸭子中,飞行的动作可能还有多种变化..此时,你可能正期盼着设计模式能骑着白马来解救你苦难的一天。但是,如果直接告诉你答案,这有什么乐趣?我们会用老方法找出一个解决之道:「采用良好的OO软件设计原则」。如果能有一种建立软件的方法,好让我们需要改变软件时,可以在对既有的代码影响昀小的情况下,轻易地达成,花较少时间重新整理程序,而多让程序去做更酷的事。该有多好...8第一章好吧!在软件开发上,有什么是你可以深信不疑的?不管你在何处工作,建造些什么,用何种程序语言,在软件开发上,有没有一个不变的真理?软件开发的一个不变真理CHANGE(用镜子来看答案)不管当初软件设计得多好,一阵子之后,总是需要成长与改变,否则软件就会「死亡」。驱使改变的因素很多。找出你的应用中需要改变代码的地方,一一列出来。(我们先起个头,好让你有个方向。)我们的顾客或用户需要别的东西,或者想要新功能。我的公司决定采用别的数据库产品,也从另一家厂商买了不同的数据,这造成数据格式不兼容。唉!不变的是变化介紹設計模式目前位置9现在我们知道使用继承有一些缺失,因为改变鸭子的行为会影响所有种类的鸭子,而这并不恰当。Flyable与Quackable接口一开始似乎还挺不错,解决了问题(只有会飞的鸭子才继承Flyable),但是Java的接口不具有实现代码,所以继承接口无法达到代码的复用。这意味着:无论何时你需要修改某个行为,你被迫得往下追踪并修改每一个有定义此行为的类,一不小心,可能造成新的错误。幸运地,有一个设计原则,正适用于此状况。把问题归零换句话说,如果每次新的需求一来,都会变化到某方面的代码,那么你就可以确定,这部分的代码需要被抽出来,和其他闻风不动的代码有所区隔。下面是这个原则的另一个思考方式:「把会变化的部分取出并封装起来,以便以后可以轻易地扩充此部分,而不影响不需要变化的其他部分」。这样的概念很简单,几乎是每个设计模式背后的精神所在。所有的模式都提供了一套方法让「系统中的某部分改变不会影响其他部分」。好,该是把鸭子的行为从Duck类中取出的时候了!把会变化的部分取出并「封装」起来,好让其他部分不会受到影响。结果如何?代码变化之后,出其不意的部分变得很少,系统变得更有弹性。设计原则找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。这是我们的第一个设计原则,以后还有更多原则会陆续在本书中出现。10第一章分开变化和不会变化的部分鸭子类Duck类仍是所有鸭子的超类,但是飞行和呱呱叫的行为已经被取出,放在别的类结构中。多种行为的实现被放在这里。现在飞行和呱呱叫都有它们自己的类。鸭子行为呱呱叫行为飞行行为取出易于变化的部分如何开始?就我们目前所知,除了fly()和quack()的问题之外,Duck类还算一切正常,似乎没有特别需要经常变化或修改的地方。所以,除了某些小改变之外,我们不打算对Duck类做太多处理。现在,为了要分开「变化和不会变化的部分」,我们准备建立两组类(完全远离Duck类),一个是「fly」相关的,一个是「quack」相关的,每一组类将实现各自的动作。比方说,我们可能有一个类实现「呱呱叫」,另一个类实现「吱吱叫」,另一个类实现「安静」。我们知道Duck类内的fly()和quack()会随着鸭子的不同而改变。为了要把这两个行为从Duck类中分开,我们将把它们自Duck类中取出,建立一组新类代表每个行为。抽出變動的部分介紹設計模式目前位置11如何设计类实现飞行和呱呱叫的行为?我们希望一切能有弹性,毕竟,正是因为一开始的鸭子行为没有弹性,才让我们走上现在这条路。我们还想能够「指定」行为到鸭子的实例,比方说,想要产生绿头鸭实例,并指定特定「类型」的飞行行为给它。干脆顺便让鸭子的行为可以动态地改变好了。换句话说,我们应该在鸭子类中包含设定行为的方法,就可以在「运行时」动态地「改变」绿头鸭的飞行行为。有了这些目标要达成,接着看看第二个设计原则:设计鸭子的行为设计原则针对接口编程,而不是针对实现编程。我们利用接口代表每个行为,比方说,FlyBehavior与QuackBehavior,而行为的每个实现都必须实现这些接口之一。所以这次鸭子类不会负责实现Flying与Quacking接口,反而是由其他类专门实现FlyBehavior与QuackBehavior,这就称为「行为」类。由行为类实现行为接口,而不是由Duck类实现行为接口。这样的作法迥异于以往,以前的作法是:行为是继承自Duck超类的具体实现而来,或是继承某个接口并由子类自行实现而来。这两种作法都是依赖于「实现」,我们被实现绑得死死的,没办法更改行为(除非写更多代码)。在我们的新设计中,鸭子的子类将使用接口(FlyBehavior与QuackBehavior)所表示的行为,所以实际的「实现」不会被绑死在鸭子的子类中。(换句话说,特定的实现代码,是位于实现FlyBehavior与QuakcBehavior的特定类中)。从现在开始,鸭子的行为将被放在分开的类中,此类专门提供某行为的实现。如此,鸭子类就不再需要知道行为的实现细节。interfaceFlyBehaviorfly()fly(){//实现鸭子的飞行动作}FlyWithWingsfly(){//什么都不做,不会飞!}FlyNoWay12第一章这里所谓的「接口」有多个含意,接口是一个「概念」,也是一种Java的interface构造。你可以在不涉及Javainterface的情况下,「针对接口编程」,关键就在多态。利用多态,程序可以针对超类型编程,执行时会根据实际状况执行到真正的行为,不会
本文标题:项目架构资料
链接地址:https://www.777doc.com/doc-809798 .html