软件设计与体系结构 5-设计模式-1_第1页
软件设计与体系结构 5-设计模式-1_第2页
软件设计与体系结构 5-设计模式-1_第3页
软件设计与体系结构 5-设计模式-1_第4页
软件设计与体系结构 5-设计模式-1_第5页
已阅读5页,还剩115页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

5-设计模式概述我们常常听说设计模式,在面试时面试官会问到这样的题目;在设计和coding中,也会用到。一些设计模式书籍,比如“四人帮”的《设计模式》、浅显易懂的《HeadFirst设计模式》。设计模式是对类似业务场景的解决方案的总结。那你是否觉得自己已经掌握了设计模式呢?是否思考过怎么才算真正掌握了设计模式呢?是熟练掌握每种设计模式的原理和代码实现吗?搞懂23种经典的设计模式,并不是件难事。难的是如何不生搬硬套、恰到好处地将其应用到实际的项目中。单纯学习设计模式,并不能让你写出更好的代码。这就像单纯地了解编程语言的语法,也不能算是会写代码一样。单纯看书,对于设计模式的掌握、代码能力的锻炼,你只能达到10%,剩下的90%还是要靠在实战中刻意练习。Withpatterns,youcanusethesolutionamilliontimesover,withouteverdoingitthesamewaytwice.(利用模式,我们可以让一个解决方案重复使用,而不是重复造轮子。)——克里斯托弗•亚历山大概述设计模式是语言无关的最佳设计实践准则而不是什么真理、定理。为什么要学习设计模式?是为了应对面试、告别烂代码、编写易扩展+易用+易维护的高质量代码、读源码、学框架、胜任技术leader等等。23种经典设计模式共分为3种类型,分别是创建型、结构型和行为型。在软件工程领域,设计模式是一套通用的可复用的解决方案,用来解决在软件设计过程中产生的通用问题。它不是一个可以直接转换成源代码的设计,只是一套在软件系统设计过程中程序员应该遵循的最佳实践准则。概述-设计模式是什么?不是什么?Agenda01设计模式之创建型02设计模式之结构型03设计模式之行为型设计模式之创建型-单例模式单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建全局唯一的对象,让所有需要调用的地方都共享这一单例对象。代码是不是线程安全呢?假设Singleton类刚刚被初始化,instance空,这时候两个线程同时访问getInstance方法,因为instance是空,所以两个线程同时通过了条件判断,开始执行new操作,这样一来,显然instance被构建了两次。(懒汉模式)设计模式之创建型-单例模式1.为了防止new

Singleton被执行多次,因此在new操作之前加上Synchronized同步锁,锁住整个类。2.进入Synchronized临界区以后再做一次判空。因为当两个线程同时访问的时候,线程A构建完对象,线程B也已经通过了最初的判空验证,不做第二次判空的话,线程B还是会再次构建instance对象。设计模式之创建型-单例模式像这样两次判空的机制叫做双重检测机制。可是这段代码仍然不是绝对的线程安全(JVM指令重排)设计模式之创建型-单例模式设计模式之创建型-工厂模式工厂模式包括简单工厂、工厂方法、抽象工厂这3种细分模式。其中,简单工厂和工厂方法比较常用,抽象工厂的应用场景比较特殊,所以很少用到。工厂模式用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。实际上,如果创建对象的逻辑并不复杂,那我们直接通过new来创建对象就可以了,不需要使用工厂模式。当创建逻辑比较复杂,是一个“大工程”的时候,我们就考虑使用工厂模式,封装对象的创建过程,将对象的创建和使用相分离。设计模式之创建型-工厂模式自然会有这样的疑问:“既然都有了构造函数,何必再折腾那么多事情呢”?Factroy要解决的问题是:希望能够创建一个对象,但创建过程比较复杂,希望对外隐藏这些细节。请特别留意“创建过程比较复杂“这个条件。如果不复杂,用构造函数就够了。比如你想用一个HashMap时也要用一个factory,就无必要。那什么是“复杂的创建过程呢“?举几个例子:例子1:创建对象可能是一个pool里的,不是每次都凭空创建一个新的。而pool的大小等参数可以用另外的逻辑去控制。比如连接池对象,线程池对象就是个很好的例子。例子2:对象代码的作者希望隐藏对象真实的的类型,而构造函数一定要真实的类名才能用。比如作者提供了:但他不希望你知道FooImplV1的存在(没准下次就改成V2了),只希望你知道Foo,所以他必须提供某种类似于这样的方式让你用

设计模式之创建型-工厂模式设计模式之创建型-工厂模式要点是,当你有任何复杂的的创建对象过程时,你都需要写一个某种createXXXX的函数帮你实现。再拓展一下范围,哪怕创建的不是对象,而是任何资源,也都得这么干。一句话:不管你用什么语言,创建什么资源。当你开始为“创建”本身写代码的时候,就是在使用“工厂模式”了。具体形式可以根据当时的场景去调整,不管你用的是静态函数,抽象类还是模版等,那都是细节。不同语言的支持也不太一样。设计模式之创建型-工厂模式不要为了工厂模式而工厂模式。搞工厂这么一套都是有成本的。更麻烦的是假如你一开始搞错了,做出来的工厂的接口抽象后来发现是不符合需求变更,改起来还不如一开始没有做工厂,直接new。越简单的代码越容易改,哪怕看起来会有些体力劳动,但不费神。当然,这也不是说尽量不要用模式。这完全取决于你对需求的理解。所以多花时间理解需求和业务,然后问自己“这里可能会变得很复杂吗?这里未来3个月多大可能需要扩展?”设计模式之创建型-工厂模式同时也不要照着《设计模式》去写代码。你可以将《设计模式》理解为是一本字典。它的内容是没错,但一般只用来做参考。对于一个模式要不要用?怎么用?要看场景。正常写文章的人,除非是学生,没人会在写文章的时候抱着本字典去写。(1)简单工厂模式设计模式之创建型-工厂模式设计模式之创建型-工厂模式简单工厂模式提供专门的工厂类用于创建对象,实现了对象创建和使用的职责分离,客户端只需知道具体产品类所对应的参数即可:通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。但缺点在于不符合“开闭原则”,每次添加新产品就需要修改工厂类。在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展维护,并且工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。为了解决简单工厂模式的问题,出现了工厂方法模式。设计模式之创建型-工厂模式工厂方法模式将工厂抽象化,并定义一个创建对象的接口。每增加新产品,只需增加该产品以及对应的具体实现工厂类由具体工厂类决定要实例化的产品是哪个,将对象的创建与实例化延迟到子类,这样工厂的设计就符合“开闭原则”了,扩展时不必去修改原来的代码。但缺点在于,每增加一个产品都需要增加一个具体产品类和实现工厂类,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。设计模式之创建型-工厂模式设计模式之创建型-原型模式原型模式(PrototypePattern)是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。即用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。工作原理:将原型对象传给那个要发动创建的对象,这个要发动创建的对象通过:请求原型对象拷贝它们自己来实施创建。即用基类Object的clone()方法或序列化。设计模式之创建型-原型模式对于熟悉JavaScript语言的前端程序员来说,原型模式是一种比较常用的开发模式。JavaScript是一种基于原型的面向对象编程语言。即便JavaScript现在也引入了类的概念,但它也只是基于原型的语法糖而已。在Java语言中,Object类实现了Cloneable接口,一个对象可以通过调用Clone()方法生成对象,这就是原型模式的典型应用。设计模式之创建型-原型模式绝大多数设计模式都是牺牲性能、提升开发效率的,原型模式则是为数不多的牺牲开发效率、提升性能的设计模式。原型模式的使用场景:1)资源优化场景。2)类初始化需要很多资源。3)性能和安全要求的场景。4)通过new产生一个对象需非常繁琐的数据准备或访问权限。5)一个对象多个修改者的场景。6)一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。常见的应用场景比如spring中的bean对象创建:在Spring中,用户可以采用原型模式来创建新的bean实例,从而实现每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式又可分为浅拷贝和深拷贝,区别在于对引用数据类型的成员变量的拷贝。设计模式之创建型-原型模式设计模式之创建型-原型模式Java中的数据类型,分为基本类型和引用类型。在一个方法里的变量如果是基本类型的话,变量就直接存储在这个方法的栈帧里,例如int、long等;而引用类型则在栈帧里存储这个变量的指针,指向堆中该实体的地址,例如String、Array等。深拷贝和浅拷贝是只针对引用数据类型的。比如一个方法有一个基本类型参数和一个引用类型参数,在方法体里对参数重新赋值,会影响传入的引用类型参数,而不会影响基本类型参数,因为基本类型参数是值传递,而引用类型是引用传递。设计模式之创建型-原型模式当类的成员变量是基本数据类型时,浅拷贝会将原对象的❶属性值赋值给新对象。当类中成员变量时引用数据类型时,浅拷贝会将原对象的引用数据类型的❷地址赋值给新对象的成员变量。也就是说两个对象共享了同一个数据。当其中一个对象修改成员变量的值时,另外一个的值也会随之改变。深拷贝对于基本数据类型还是引用数据类型,都会去开辟额外的空间给新对象。不会出现浅拷贝中存在的问题。当一个类中只有基本数据类型时,浅拷贝与深拷贝是同样的。当一个类中含有引用数据类型是,浅拷贝只是拷贝一份引用,修改浅拷贝的值,原来的也会跟着变化。(1)浅拷贝类中为基本数据类型,String不是基本数据类型,但是因为String是final修饰的。改变这个值其实是new一个新String对象,所以不会影响之前的原对象。设计模式之创建型-原型模式设计模式之创建型-原型模式修改clone对象的值不影响原对象的值。如果类中含有引用数据类型的对象,此时再添加一个引用数据类型的对象Skill。设计模式之创建型-原型模式设计模式之创建型-原型模式设计模式之创建型-原型模式(2)深拷贝有两种实现方式:第一种使用两层浅拷贝实现。此时我们使用深拷贝。将Skill对象也复制一份。其实这种方式也是属于浅层次的拷贝,因为Skill类中数据类型是基本数据类型。如果Skill类中还有一层引用数据类型的话,那还需要在clone方法中在添加一层,才可以实现真正的深拷贝。不太推荐使用这种方式。第二种是使用序列化对象实现深拷贝。Engineer和Skill需要实现Serializable接口。设计模式之创建型-原型模式设计模式之创建型-原型模式设计模式之创建型-原型模式原型模式总结:创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率可以不用重新初始化对象,动态地获得对象运行时的状态。如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码若成员变量无引用类型,浅拷贝clone即可;若引用类型的成员变量很少,可考虑递归实现clone,否则推荐序列化。Agenda01设计模式之创建型02设计模式之结构型03设计模式之行为型结构型模式主要总结了一些类或对象组合在一起的经典结构,这些经典的结构可以解决特定应用场景的问题。代理模式在不改变原始类接口的条件下,为❶原始类定义一个❷代理类主要目的是控制访问,而非加强功能,这是它跟装饰器模式最大的不同。一般情况下,我们让代理类和原始类实现同样的接口。代理模式(ProxyPattern)为其他对象提供一种代理以控制对这个对象的访问。设计模式之结构型-代理模式设计模式之结构型-代理模式但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的。在这种情况下,我们可以通过让代理类继承原始类的方法来实现代理模式。静态代理需要针对每个类都创建一个代理类,并且每个代理类中的代码都有点像模板式的“重复”代码,增加了维护成本和开发成本。对于静态代理存在的问题,我们可以通过动态代理来解决。我们不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。设计模式之结构型-代理模式代理模式常用在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志幂等:用户不小心多点了几次,后端收到了好几次提交,这时就会在数据库中重复创建了多条记录。这就是接口没有幂等性带来的bug。我们将这些附加功能与业务功能解耦,放到代理类统一处理,让程序员只需要关注业务方面的开发。除此之外,代理模式还可以用在RPC、缓存等应用场景中。设计模式之结构型-代理模式定义总是抽象而晦涩难懂的,让我们回到生活中来吧。实例:程序猿所在的公司(宇宙软件有限公司)老板突然在发工资的前一天带着小姨子跑路了,可怜程序猿一身房贷,被迫提起劳动仲裁。劳动局就会为其指派一位代理律师全权负责程序猿的仲裁事宜。那这里面就是使用了代理模式。因为在劳动仲裁这个活动中,代理律师会全权代理程序猿。1)静态代理静态代理是指预先确定了代理与被代理者的关系,例如程序猿的代理律师方文镜是在开庭前就确定的了。那映射到编程领域的话,就是指代理类与被代理类的依赖关系在编译期间就确定了。程序猿劳动仲裁的代码实现:首先定义一个代表诉讼的接口。设计模式之结构型-代理模式设计模式之结构型-代理模式Plaintiff原告设计模式之结构型-代理模式设计模式之结构型-代理模式设计模式之结构型-代理模式可以看到,代理律师全权代理了程序猿的本次诉讼活动。那使用这种代理模式有什么好处呢,我们为什么不直接让程序猿直接完成本次诉讼呢?现实中的情况比较复杂,简单列出几条:这样代理律师就可以在提起诉讼等操作之前做一些校验工作,或者记录工作。例如程序猿提供的资料,律师可以选择的移交给法庭而不是全部等等操作,就是说可以对代理的事情做一些控制。例如程序猿不能出席法庭,代理律师可以代为出席。设计模式之结构型-代理模式(2)动态代理动态代理本质上仍然是代理,情况与上面介绍的完全一样,只是代理与被代理人的关系是动态确定的例如程序猿的同事程序媛开庭前没有确定她的代理律师,而是在开庭当天当庭选择了一个律师,映射到编程领域为这个关系是在运行时确定的。那既然动态代理没有为我们增强代理方面的任何功能,那我们为什么还要用动态代理呢,静态代理不是挺好的吗?凡是动态确定的东西大概都具有灵活性,强扩展的优势。设计模式之结构型-代理模式上面的例子中如果程序媛也使用静态代理的话,那么就需要再添加两个类。一个是程序媛诉讼类,一个是程序媛的代理律师类,还的在代理静态工厂中添加一个方法。而如果使用动态代理的话,就只需要生成一个诉讼类就可以了,全程只需要一个代理律师类。因为我们可以动态的将很多人的案子交给这个律师来处理。设计模式之结构型-代理模式在Java的动态代理机制中,有两个重要的类或接口一个是InvocationHandler接口、另一个则是Proxy类,这个类和接口是实现我们动态代理所必须用到的。InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的,而Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。接下来我们看下实例,程序媛动态指定代理律师是如何实现的。设计模式之结构型-代理模式设计模式之结构型-代理模式设计模式之结构型-代理模式桥接模式的代码实现非常简单,但是理解起来稍微有点难度,并且应用场景也比较局限,桥接模式在日常开发中不是特别常用。主要是因为上手难度较大,但是对于理解面向对象设计有非常大的帮助。桥接模式有两种理解方式。桥接模式(BridgePattern)是将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(HandleandBody)模式或接口(Interface)模式。设计模式之结构型-桥接模式设计模式之结构型-桥接模式第一种理解方式是“将抽象和实现解耦,让它们能独立开发”。这种理解方式比较特别,应用场景也不多。另一种理解方式更加简单,等同于“组合优于继承”设计原则,这种理解方式更加通用,应用场景比较多。不管是哪种理解方式,它们的代码结构都是相同的,都是一种类之间的组合关系。设计模式之结构型-桥接模式(2)使用场景桥我们大家都熟悉,顾名思义就是用来将河的两岸联系起来的。而此处的桥是用来将两个独立的结构联系起来,而这两个被联系起来的结构可以独立的变化,所有其他的理解只要建立在这个层面上就会比较容易。如果一个系统需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系。通过桥接模式可以使它们在抽象层建立一个关联关系。设计模式之结构型-桥接模式(2)使用场景❶“抽象部分”和❷“实现部分”可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对❶抽象化角色和❷实现化角色进行动态耦合。一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。如果不希望使用继承、或因为多层继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。首先定义一个点咖啡接口,里面有一个下单方法,至于点哪种口味的咖啡,就由其子类去决定,这是很容易想到的。设计模式之结构型-桥接模式客户要求加两个容量规格的咖啡:大杯和小杯。幸好是面向抽象编程的,对应加几个实现类就可以解决问题。设计模式之结构型-桥接模式设计模式之结构型-桥接模式如果你是程序员,写着写着就会感到不安:共需要3x2=6个类,即大杯原味和加糖,中杯原味和加糖,小杯原味和加糖。过段时间万一客户又要出加奶、加蜂蜜等等口味,说不定还有迷你杯、女神杯等规格的咖啡,那这边的类不就爆炸了吗?看来得去找个设计模式了。此场景桥接模式正合适,这里有两个变化维度即咖啡的容量和口味,而且都需要独立变化。如果使用继承的方式,随着变化,类就会急剧的增加。你可以将容量理解为抽象部分,而口味理解为实现部分,这两个部分需要桥接。设计模式之结构型-桥接模式抽象化角色就像是一个水杯的手柄,而具体实现化角色就像是水杯的杯身。手柄控制杯身,这就是此模式别名“柄体”的来源。接下来用桥接模式重构代码,分析当前业务场景,可以将咖啡的容量作为抽象化Abstraction,而咖啡口味为实现者Implementor。Coffee持有了ICoffeeAdditives引用,ICoffeeAdditives的实例是通过构造函数注入的,这个过程就是我们所说的桥接过程。我们通过这个引用就可以调用ICoffeeAdditives的方法,进而将Coffee的行为与ICoffeeAdditives的行为通过orderCoffee()方法而组合起来。

设计模式之结构型-桥接模式下面是一个对抽象化修正的一个类,里面增加了一个品控的方法。设计模式之结构型-桥接模式设计模式之结构型-桥接模式通过使用桥接模式,就使得咖啡的容量和口味这两个维度可以独立变化,互不干扰。设计模式之结构型-桥接模式设计模式之结构型-桥接模式优点:分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系”解耦:抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自维度的变化,也就是说抽象和实现不再在同一个继承层次结构中,而是“子类化”它们,使它们各自都具有自己的子类,以便任何组合子类,从而获得多维度组合对象。设计模式之结构型-桥接模式在很多情况下,桥接模式可以取代多层继承方案然而,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”。设计模式之结构型-桥接模式缺点:桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累。装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承,给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。除此之外,装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这样的需求,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口。装饰器模式(DecoratorPattern)是在不必改变原始类和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。设计模式之结构型-装饰器模式设计模式之结构型-装饰器模式(2)使用场景需要在运行时动态的给一个对象增加额外的职责时候;需要给一个现有的类增加职责,但是又不想通过继承的方式来实现的时候(应该优先使用组合而非继承),或者通过继承的方式不现实的时候(可能由于排列组合产生类爆炸的问题)。设计模式之结构型-装饰器模式在上一节桥接模式中提到的85度咖啡系统项目。程序猿在攻城狮的指导下使用桥接模式实现了星巴克要求的各种下单功能:大杯原味、大杯加糖、大杯加奶;中杯原味、中杯加糖、中杯加奶;小杯原味、小杯加糖、小杯加奶。刚舒服了没两天,项目经理就来找程序猿了:现在用户的口味太刁,好多都要同时加奶,加糖,有的还要加蜂蜜,咱们目前这个实现不支持,你去改一下。对了,有些用户要求先加糖再加奶,而一些用户要求先加奶然后再加糖,顺序不能乱!设计模式之结构型-装饰器模式正当程序猿一筹莫展的时候,攻城狮兴高采烈的走了过来,程序猿说:“我这个有个棘手的问题请教你一下”。攻城狮开始分析需求:假设我们有一个原味咖啡的类OriginalCoffee,目前的需求就是要动态的给这个类的一些实例增加一些额外功能,此处就是动态的对某些咖啡制作过程增加新的流程例如加奶、加糖,而有的咖啡却保持原味不变。这种需求要是通过继承的方式就不太好实现,因为咖啡制作过程是动态变化的。例如有的需要原味咖啡,有的需要加奶咖啡,有的需要加糖咖啡,而有的需要先加奶再加糖咖啡,而有的需要先加糖再加奶的咖啡,等等不同需求。设计模式之结构型-装饰器模式这是一个排列组合问题,如果使用类继承的方式实现,必须预先将所有可能组合都想清楚,然后生成相应的子类随着咖啡口味的增多,以及添加顺序的改变,几乎是不可扩展和维护的。经过需求分析,攻城狮锁定了装饰者模式来实现此需求。原味咖啡是本质,而加奶、加糖都是在装饰这个本质的东西,再怎么加东西,咖啡还是咖啡。设计模式之结构型-装饰器模式首先我们有一个ICoffee接口,里面有一个制作咖啡的接口方法makeCoffee()。要进行装饰的类OriginalCoffee和装饰者基类CoffeeDecorator(一般为抽象类)实现了此接口。CoffeeDecorator类里面持有一个ICoffee引用,我们第一步会把要装饰那个原始对象赋值给这个引用,那样在装饰者类中才可以调用到那个被装饰的对象的方法。MilkDecorator和SugarDecorator都继承至CoffeeDecorator,都是具体的装饰者类。设计模式之结构型-装饰器模式设计模式之结构型-装饰器模式设计模式之结构型-装饰器模式设计模式之结构型-装饰器模式设计模式之结构型-装饰器模式设计模式之结构型-装饰器模式装饰者模式的精髓在于动态的给对象增减功能。当你你需要原味咖啡时,那就生成原味咖啡的对象,而当你需要加奶咖啡时,仅仅需要将原味咖啡对象传递到加奶装饰者中去装饰一下就好了。如果你加了奶还想加糖,那就把加了奶的咖啡对象丢到加糖装饰者类中去装饰一下,一个先加奶后加糖的咖啡对象就出来了设计模式之结构型-装饰器模式优点:可以提供比继承更加灵活的方式去扩展对象的功能,通过排列组合,可以对某个类的一些对象做动态的功能扩展,而不需要装饰的对象却可以保持原样。缺点仍然是设计模式的通病。缺点:类的个数会增加,会产生很多装饰者类,相应的就增加了复杂度。设计模式之结构型-装饰器模式装饰者模式与代理模式的区别:一般认为代理模式侧重于使用代理类增强被代理对象的访问,而装饰者模式侧重于使用装饰者类来对被装饰对象的功能进行增减。除了上面的区别,还有装饰者模式主要是提供一组装饰者类,然后形成一个装饰者栈,来动态的对某一个对象不断加强,而代理一般不会使用多级代理。由于面向对象程序设计本身就是从实际生活中汲取的灵感,将大千世界抽象到程序设计领域,所以所有的设计模式都是可以在日常生活中感受的到的。例如适配器模式,这个在日常生活中就太普遍了。例如我们程序员经常遇到的电脑上提供的端口与要插入的接头匹配不上,而我们则可以通过一个中间的适配器将两边连接起来。适配器模式(AdapterPattern)是将一个接口转换为客户端所期待的接口,从而使两个接口不兼容的类可以在一起工作。设计模式之结构型-适配器模式设计模式之结构型-适配器模式代理模式、装饰器模式提供的都是跟原始类相同的接口,而适配器提供跟原始类不同的接口。适配器模式是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。适配器模式有两种实现方式:类适配器和对象适配器。类适配器使用继承关系来实现,对象适配器使用组合关系来实现。适配器模式是一种事后的补救策略,用来补救设计上的缺陷。应用这种模式算是“无奈之举”。(2)使用场景如果在设计初期,我们就能规避接口不兼容的问题,那这种模式就无用武之地了。在实际的开发中,什么情况下才会出现接口不兼容呢?总结下面这5种场景:封装有缺陷的接口设计统一多个类的接口设计替换依赖的外部系统兼容老版本接口适配不同格式的数据适配器模式还有个别名叫:Wrapper(包装器),顾名思义就是将目标类用一个新类包装一下,相当于在客户端与目标类直接加了一层。IT世界有句俗语:没有什么问题是加一层不能解决的。

设计模式之结构型-适配器模式设计模式之结构型-适配器模式最近程序猿的工作上遇到了一件非常不开心的事:由于老项目中的日志系统非常粗糙,导致他debug时候非常吃力。他调研了一个非常棒的三方开源日志库来改进项目的log系统,但是此开源库与项目中接口不兼容,这几乎是肯定的所以他又一次展现出了其高超的程序设计能力,精准的采用了适配器模式来完成这个功能。设计模式之结构型-适配器模式设计模式之结构型-适配器模式设计模式之结构型-适配器模式设计模式之结构型-适配器模式(4)优缺点适配器LogAdapter必须要实现目标接口,且依赖那个提供功能的类型,此处为NbLogger。优点是极大的增强了程序的可扩展性,通过此模式,你可以随意扩展程序的功能,但却不需要修改接口。缺点是这个模式真没啥缺点,唯一可以称的上的缺点是多了一层,但是这也是没有办法的事情。门面模式(又称为外观模式)原理、实现都非常简单,应用场景比较明确。它的思想非常简单,对外提供简单的交互接口,隐藏内部的复杂性即通过封装细粒度的接口,提供组合各个细粒度接口的高层次接口,来提高接口的易用性,或者解决性能、分布式事务等问题。外观模式(FacadePattern)提供一个高层次的接口,使得子系统更易于使用。设计模式之结构型-门面模式设计模式之结构型-门面模式(2)使用场景当你的程序有很多个模块,或者说子系统。你希望给用户提供一个统一的操作界面类,而不是让用户分别与这些模块交互。一种设计是:给用户提供一套面板方用来法完成一般性的功能,如果面板提供的方法不能满足用户需求,用户还可以直接使用具体模块的功能来达到自己的目的。另一种设计是:用户完全看不到各子模块,所有操作必须通过面板来完成。设计模式之结构型-门面模式程序猿的公司大老板最近突然对他们正在开发的商城项目感了兴趣,所以要求他们出各种各样的报表。这可苦了程序猿他们了因为大老板要求的报表数据来自不同的子系统,这些子系统相互之间还有依赖,要想获得一份完整的报表,需要访问好多个模块,先调这个API再调那个API,前前后后调用了十几个API,涉及好几个系统。程序猿他们也不敢说话,确实比较扯,最后程序猿决定要扭转这种不利的局面,经过分析,他选择了外观设计模式。设计模式之结构型-门面模式设计模式之结构型-门面模式第二步,创建外观类。这个外观类就是引用其他功能模块,完成相关的功能对外暴露操作接口。例如本例中的外观类ReportFacade使用了OrderSys、PaymentSys与DeliverySys3个子模块来完成一个生成报表的功能客户端只需要通过此方法就可以生成报表了,不用关心那些子模块。

设计模式之结构型-门面模式设计模式之结构型-门面模式设计模式之结构型-门面模式可见客户端只于外观类交互,完全不用与具体的子模块交互。技术要点总结过于简单基本没啥说的,唯一注意的一点是大部分的外观模式没有限制客户端直接使用子系统。也就是说如果用户遇到了通过外观方法无法完成的功能,可以自己调用相关子系统去完成。该模式的优点是极大的降低了客户端的复杂性;缺点是增加了一层,所以当子系统改变时,外观类相应的需要维护。(2)使用场景组合模式出镜率不算特别高,但是一旦出镜说明这个问题如果不使用它将变得非常困难。组合模式跟我们之前讲的面向对象设计中的“组合关系(通过组合来组装两个类)”,完全是两码事。这里讲的“组合模式”,主要是用来处理树形结构数据。正因为其应用场景的特殊性,数据必须能表示成树形结构,这也导致了这种模式在实际的项目开发中并不那么常用。组合模式(CompositePattern)允许以相同的方式处理单个对象和对象的组合体。设计模式之结构型-组合模式设计模式之结构型-组合模式一旦数据满足树形结构,应用这种模式就能发挥很大的作用,能让代码变得非常简洁。组合模式的设计思路,与其说是一种设计模式,倒不如说是对业务场景的一种数据结构和算法的抽象。其中,数据可以表示成树这种数据结构,业务需求可以通过在树上的递归遍历算法来实现。组合模式,将一组对象组织成树形结构,将单个对象和组合对象都看作树中的节点,以统一处理逻辑,并且它利用树形结构的特点,递归地处理每个子树,依次简化代码实现。设计模式之结构型-组合模式因此,当你的程序结构有类似树一样的层级关系时:例如文件系统、视图树、公司组织架构等;当你要以统一的方式操作单个对象和由这些对象组成的组合对象的时候,可以是用该模式。我们使用组合模式中所谓的透明方式,因为我们将单个对象和组合对象按照完全一样的事物对待了,所以接口对外很透明。统一操作都是在Component中定义的,所有继承至它的节点都要实现,而有些操作叶子节点是不支持的,例如添加移除节点等,这样就要求叶子节点处理好这些方法。设计模式之结构型-组合模式程序猿最近比较烦,公司新接了个外包IT项目,甲方是传统行业的老板,总是感觉这么简单的功能为什么要那么多钱,关键还慢...这不,刚刚要求程序猿写一个"小软件"用来管理公司的组织架构...程序猿本来还想沟通下需求:王老板,这个活可大可小,您是不是想要...把王老板烦的啊:小猿啊,你别说了,我给你3天时间你去先做一个我看看,不行就再改嘛...需求没给就...程序猿只好陪着笑脸说那我试试吧...他想出了个设计方案,此案例使用组合模式可解。设计模式之结构型-组合模式设计模式之结构型-组合模式第二,组合类此类持有一个List<OrganizationComponent>,并继承OrganizationComponent。

设计模式之结构型-组合模式第三步,叶子节点就是单个对象了,我们要使用合适的方式处理那些叶子节点不支持的对外接口方法。因为用户使用的时候只会看到对外暴露的统一接口,他不知道此对象是叶子节点还是组合对象。设计模式之结构型-组合模式设计模式之结构型-组合模式设计模式之结构型-组合模式第四步,我们来看看是否达到了我们的设计目的:以统一的接口操作单个对象与其组合对象。首先构建一个组合对象。模拟构建一家公司,公司下设行政部门和IT部门,还有一个天津分公司,而天津分公司又下设一个行政部门和IT部门。然后查询这个公司任何部门的员工人数。我们可以看到在查询过程中,我们没有判断当前对象到底是什么部门对象,都是以统一的接口在操作。设计模式之结构型-组合模式设计模式之结构型-组合模式设计模式之结构型-享元模式也许你会觉得享元模式比较陌生,但是相信在你的软

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论