设计模式概念:一套被反复使用、多数人知晓、经过分类编目的优秀代码设计经验的总结。
设计模式要素:模式名称、问题、举例、末态环境、推理、其他有关模式、已知的应用。
设计模式分类:创建型、结构型、行为型。
创建型模式功能:1.统所使用的具体类的信息封装起来;2.类的实例是如何被创建和组织的。
创建型模式作用:1.封装创建逻辑,不仅仅是new一个对象那么简单。
2.封装创建逻辑变化,客户代码尽量不修改,或尽量少修改。
常见的创建型模式:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。
常见的结构型模式:代理模式、装饰模式、适配器模式、组合模式、桥梁模式、外观模式、享元模式。
常见行为型模式:模板方法模式、命令模式、责任链模式、策略模式、迭代器模式、中介者模式、观察者模式、备忘录模式、访问者模式、状态模式、解释器模式。
单一职责原则:一个类应该只有一个职责。
优点:降低类的复杂性;提高类的可读性;提高代码的可维护性和复用性;降低因变更引起的风险。
里氏替换原则:优点:代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;提高代码的可重用性;提高代码的可扩展性;提高产品或项目的开放性。
缺点:1.继承是入侵式的。
只要继承,就必须拥有父类所有属性和方法。
2.降低代码的灵活性。
子类必须拥有父类的属性和方法,使子类收到限制。
3.增强了耦合性。
当父类的常量、变量和方法修改时,必须考虑子类的修改,这种修改可能造成大片的代码需要重构。
依赖倒置原则:高层模块不应该依赖低层模块,两者都依赖其抽象;抽象不依赖细节;细节应该依赖于抽象。
在Java中的表现:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;接口或抽象类不依赖于是实现类;实现类依赖于接口或抽象类。
接口隔离原则:1.一个类对另外一个类的依赖性应当是建立在最小的接口上的2.一个接口代表一个角色,不应当将不同的角色交给一个接口。
3.不应该强迫客户使用它们的不同方法。
如图所示的电子商务系统在三个地方会使用到订单类:一个是门户,只能有查询方法;一个是外部系统,有添加订单的方法;一个是管理后台,添加、删除、修改、查询都要用到。
“原子”在实践中的衡量规则:1.一个接口只对一个子模块或者业务逻辑进行分类。
2.只保留接口中业务逻辑需要的public方法。
3.尽量修改污染了的接口,若修改的风险较大,则可采用适配器模式进行转化处理。
4.接口设计应因项目而异,因环境而异,不能照搬教条。
迪米特法则:(表述)只与你直接的朋友们通信;不要跟“陌生人”说话;每一个软件单位对其他的单位都只有最少的了解,这些了解仅局限于那些与本单位密切相关的软件单位。
对迪米特法则进行模式设计有两个:外观模式、中介者模式。
开闭原则:一个软件实体应当对扩展开放,对修改关闭。
重要性体现:提高复用性;提高维护性;提高灵活性;易于测试单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。
优点:1.在内存中只有一个实例,减少了内存的开支。
2.只生成一个实例,减少了系统的性能开销。
3.避免对资源的多重占用。
4.可以在系统设置全局的访问点,优化和共享资源访问。
缺点:1.无法创建子类,扩展困难,若要扩展,除修改代码基本上没有第二种途径可以实现。
2.对测试不利。
3.与单一职责原则有冲突。
饿汉式单例类与懒汉式单例类之间的区别:1.饿汉式单例类在被加载时实例化,而懒汉式单例类在第一次引用时实例化。
2.从资源利用效率上说,饿汉式单例类要差一点,但从速度和反应时间的角度来讲,饿汉式单例类则比懒汉式单例类稍好些。
3.饿汉式单例类可以在Java中实现,但不易在C++中实现。
单例模式的使用场景:1.要求生成唯一的序列号环境。
2.在整个项目中需要一个共享访问点或共享数据。
3.创建一个对象需要消耗的资源过多。
4.需要定义大量的静态方法的环境。
使用单例模式的注意事项:1.在使用任何EJB、RMI和JINI的分布式系统中,应当避免使用有状态的单例类。
2.同一个JVM中会有多个类加载器,当两个类加载器同时加载一个类时,会出现两个实例,此时也应当尽量避免使用有状态的单例类。
工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化那个类。
优点:良好的封装性,代码结构清晰;优秀的可扩展性;屏蔽产品类;典型的解耦框架。
使用场景:1.工厂方法模式是new一个对象的替代品,故在所有需要生成对象的地方都可以使用,但是需要慎重考虑是否需要增加一个工厂类进行管理,增加代码的复杂度。
2.需要灵活的、可扩展的框架时。
3.可以用在异构项目中。
4.可以使用在测试驱动开发的框架下。
抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,且无需指定它们的具体类。
优点: 1.产品族内的约束为非公开状态,在不同的工厂中,各种产品可能具有不同的相互依赖关系,这些依赖关系由工厂封装在其内部,对于工厂的使用者是不可见的。
2.生产线的扩展非常容易,如果要针对同一产品族建立新的生产线,只需要实现产品族中的所有产品接口并建立新的工厂类即可。
缺点:产品族本身的扩展非常困难,如果需要在产品族中增加一个新的产品类型,则需要修改多个接口,并且会影响已有的工厂类。
使用场景:当一个对象族(或是一组没有任何关系的对象)都有相同的约束。
建造者将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
优点:1.封装性,可以使客户端不必知道产品内部组成的细节。
2.建造者独立,容易扩3.便于控制细节风险。
使用场景:1.相同的方法,不同的执行顺序,产生不同的结果。
2.多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时。
3.产品类非常复杂,或者产品类中的方法调用顺序不同产生了不同的效能。
4.在对象创建过程中会使用到系统的一些其他对象,这些对象在产品对象的创建过程中不易得到时。
原型模式:用原型实例制定创建对象的种类,并且通过复制这些原型创建新的对象。
优点:性能优良;逃避构造函数的约束。
使用场景:资源优化场景;性能和安全要求场景;一个对象多个修改者的场景。
结构型模式:为其他对象提供一种代理以控制对这个对象的访问。
种类:远程代理、虚拟代理、保护代理、缓存代理、同步代理、智能引用代理优点:1.职责清晰:真实的角色实现实际的业务逻辑,不用关心其他非本职的事务,通过后期的代理完成附加的事务,附带的结果就是编程简洁清晰。
2.高扩展性:具体主题角色随需求不同可能有很多种,但只要实现了接口,代理类就完全可以在不做任何修改的情况下代理各种真实主题角色。
3.智能化:代理类可以在运行时才确定要去代理的真实主题,这是一种强大的功能。
使用场景:代理模式的应用非常广泛,大到一个系统框架、企业平台,小到事务处理、代码片段,随处可见代理模式的使用,例如,JavaRMI的远程调用和AOP。
装饰模式:动态的给一个对象添加一些额外的职责。
优点:装饰类和被装饰类都可以独立发展,而不会相互耦合;装饰模式是继承关系的一个替代方案;装饰模式可以动态地扩展一个实现类的功能。
使用场景:1.需要扩展一个类的功能,或给一个类增加附加功能。
2.需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
3.需要为一批类进行改装或加装功能。
适配器模式:将一个类的接口变换成客户端所期待的的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
优点:可以让两个没有任何关系的类在一起运行;增加了类的透明度;提高类的复用度;增强代码的灵活性。
使用场景:修改一个已经投产中的系统时,需要对系统进行扩展。
此时使用一个已有类,但这个类不符合系统中的接口,这是使用适配器模式是最合适的,它可以将不符合系统接口的类进行转换,转换成符合系统接口的、可以使用的类。
组合模式:将组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
优点:高层模块调用简单;节点自由增加。
缺点:不易控制树枝构件的类型;不易使用继承的方法来增加新的行为。
使用场景:1.需要描述对象的部分和整体的等级结构,如树形菜单、文件和文件夹管理。
2.需要客户端忽略个体构件和组合构件的区别,平等对待所有的构件。
桥梁模式:将抽象和现实解耦,使得两者可以独立地变化。
优点:1.抽象和现实的分离是桥梁模式的主要特点,是为了解决继承的缺点而提出的设计模式。
在该模式下,实现可以不受抽象的约束,不用绑定在一个固定的抽象层次上。
2.实现对客户的透明,客户端不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装。
使用场合:1.如果一个系统需要在构件的抽象化角色和具体角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。
2.设计要求实现化角色的任何改变不应当影响客户端,或者说实现化及角色的改变对客户端是完全透明的。
3.一个构件有多于一个抽象化角色和实现化角色,系统需要它们之间进行动态耦合。
4.不希望或不适合使用继承的场合。
外观模式:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。
优点:1.减少系统的相互依赖,所有的依赖都是对Façade对象的依赖,与子系统无关。
2.提高灵活性,不管子系统系统内部如何变化,只要不影响Façade对象,任何活动都是自由的。
3.提高安全性,Façade中未提供的方法,外界就无法访问,提高系统的安全性。
使用场景:1.为一个复杂的模块或子系统提供一个供外界访问的接口。
2.子系统相对独立,外界子系统的访问只要黑箱操作即可。
3.预防风险扩散,使用Façade进行访问操作控制。
享元模式:使用共享对象可有效地支持大量的细粒度的对象。
优点:大幅减少内存中对象的数量,降低程序内存的占用,提高性能。
缺点:1.增加了系统的复杂性,需要分出外部状态和内部状态,而且内部状态具有固化特性,不应该随外部状态改变而改变,这使得程序的逻辑复杂化。
2.将享元对象的状态外部化,而读取外部状态使得运行时间变长。
使用场景:1.系统中有大量的相似对象,这些对象耗费大量的内存。
2.细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,即对象没有特定身份。
3.需要缓冲池的场景。
模板方法模式:定义一个操作中的算法的框架,而将一些步骤延迟到子类中。
使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
优点:封装不变的部分,扩展可变的部分;提取公共部分代码,便于维护;行为由父类控制,子类实现。
应用场景:1.多个子类有公共方法,并且逻辑基本相同。
2.可以把重要的、复杂的、核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
3.重构时,模板方法模式是一个经常使用的模式,将相同的代码抽取到父类中。