[Java复习] 设计模式 Design Pattern

设计模式的六大原则

1、开闭原则(Open Close Principle)
对扩展开放,对修改关闭。

2、里氏代换原则(Liskov Substitution Principle)
任何基类可以出现的地方,子类一定可以出现。

3、依赖倒转原则(Dependence Inversion Principle)
对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)
使用多个隔离的接口,比使用单个接口要好。降低依赖,降低耦合。

5、迪米特法则(最少知道原则)(Demeter Principle)
一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)
尽量使用合成/聚合的方式,而不是使用继承。

Java的23中设计模式

分类

设计模式

创建型 5

单例模式(Singleton)、工厂方法模式(FactoryMethod)、抽象工厂模式(AbstractFactory)、建造者模式(Builder)、原型模式(Prototype)

结构型 7

代理模式(Proxy)、适配器模式(Adapter)、门面模式(Facade)、装饰器模式(Decorator)、桥接模式(Bridge)、组合模式(Composite)、享元模式(Flyweight)

行为型 11

模板方法模式(TemplateMethod)、观察者模式(Observer)、策略模式(Strategy)、解释器模式(Interpreter)、责任链模式(ChainofResponsibility)、命令模式(Command)、迭代器模式(Iterator)、调解者模式(Mediator)、备忘录模式(Memento)、状态模式(State)、访问者模式(Visitor)

------ 创建型 -----

单例模式(Singleton)

定义:

在一个JVM中,保证类只有一个实例。

应用场景:

Windows 任务管理器,回收站, Spring IoC默认创建的对象。

优缺点:

优点:节约内存,重复利用,方便管理。

缺点:线程安全性问题。

创建方式:

1. 饿汉式: 类初始化时,会立即加载该对象,线程天生安全,调用效率高。

2. 懒汉式:  类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象,具备懒加载功能。

3. 静态内部类: 结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。

4. 双重检测锁:增加volatile关键字,解决重排序问题。

5.  枚举单例: 使用枚举实现单例模式 优点:实现简单、调用效率高,枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞, 缺点没有延迟加载。

public class EnumSingleton {
    private EnumSingleton(){}
    public static EnumSingleton getInstance(){
        return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton{
        INSTANCE;
        private EnumSingleton singleton;
        //JVM会保证此方法绝对只调用一次
        Singleton(){
            singleton = new EnumSingleton();
        }
        public EnumSingleton getInstance(){
            return singleton;
        }
    }
}

如何选择单例模式:

不需要延迟加载:枚举或饿汉式,枚举更好。

需要延迟加载:静态内部类或懒汉式,静态内部类更好。

工厂模式(Factory)

工厂模式分简单工厂(不属于23种之一)工厂方法抽象工厂

简单工厂 : 用来生产同一等级结构中的任意产品。(不支持拓展增加产品)

工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)  

抽象工厂 :用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族)

简单工厂优缺点:

优点:能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。

缺点:一个工厂集合所有实例的创建,违法高内聚,责任分配,不易扩展,要改代码。

工厂方法(FactoryMethod)

核心工厂抽象出来,只负责具体工厂的接口,不负责具体创建,而将具体创建交个子类工厂去做。

抽象工厂(AbstractFactory)

用于生产不同产品族,比如Intel产品族和AMD产品族。

抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对多个产品等级结构。

建造者模式(Builder)

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

工厂模式创建单个类,建造者创建复合对象类。复合对象指类具有不同属性。

应用场景:

肯德基买套餐。汉堡,可乐不变。其他薯条,炸鸡翅组合经常变化,生成不同的套餐。

原型模式(Prototype):

原型表面该模式有一个样板实例,从这个样板对象中复制一个内部属性一致的对象,叫克隆,被复制的实例就是“原型”。

原型模式多用于复制创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。

应用场景:

Spring IoC里的创建的多例就是原型。

------ 结构型 ------

代理模式(Proxy)

通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用前处理,或调用后处理。分静态代理和动态代理。

静态代理:需要生产代理类。需要代理的接口多了,就会生成很多静态代理类。

动态代理:JDK动态代理,CGlib代理(操作字节码)

应用场景:

租房中介,Spring AOP, 事务原理,日志打印,权限控制,远程调用等等。

JDK动态代理,目标业务类必须实现接口。

具体是实现InvocationHandler接口的调用处理对象。

CGLIB动态代理,不需要委托类实现接口。操作字节码生成子类。

CGLIB动态代理与JDK动态区别

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

适配器模式(Adapter)

将一个类的接口转换成希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。别名包装器(Wrapper)。

适配器模式有两种:类适配器、对象适配器

类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。

类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。

对象适配器模式:

  • Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
  • Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
  • Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

在对象适配器中,客户端需要调用 request() 方法,而适配者类 Adaptee 没有该方法,但是它所提供的 specificRequest() 方法却是客户端所需要的。为了使客户端能够使用适配者类,需要提供一个包装类 Adapter,即适配器类。这个包装类包装了一个适配者的实例,从而将客户端与适配者衔接起来,在适配器的 request() 方法中调用适配者的 specificRequest() 方法。因为适配器类与适配者类是关联关系(也可称之为委派关系),所以这种适配器模式称为对象适配器模式。

代码实例:算法库重用

目标接口:

interface ScoreOperation {
    public int[] sort(int array[]); //成绩排序
    public int search(int array[],int key); //成绩查找
}

适配者(被适配的类):

Class QuickSort{
       sort(){…}
}
Class BinarySearch{
       binarySearch(){…}
}

适配器:

class OperationAdapter implements ScoreOperation {
     private QuickSort sortObj; //定义适配者QuickSort对象private    
     BinarySearch searchObj; //定义适配者BinarySearch对象
     public OperationAdapter() {
         sortObj = new QuickSort();
         searchObj =    new BinarySearch();
     }
     public int[] sort(int array[]) {
         //调用适配者类QuickSort的排序方  法
         return sortObj.quickSort(array); 
     }
     public int search(int array[],int key) {
         return searchObj.binarySearch(array,key); //调用适配者类BinarySearch的查找方法
     }
}

类适配器模式:

类适配器模式和对象适配器模式最大的区别在于适配器和适配者之间的关系不同,对象适配器模式中适配器和适配者之间是关联关系,而类适配器模式中适配器和适配者是继承关系.

适配器模式优缺点:

优点:1. 将目标类和适配者类解耦。 2.代码复用,兼容老系统代码

缺点:对于Java,一次只能适配一个适配者,不能同时适配多个适配者。

应用场景:

系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。

外观模式(Facade):

又叫门面模式,隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。

客户端为了完成某个功能,需要去调用某个系统中的多个模块,把它们称为是A模块、B模块和C模块吧,对于客户端而言,那就需要知道A、B、C这三个模块的功能,还需要知道如何组合这多个模块提供的功能来实现自己所需要的功能,非常麻烦。

客户端就不用跟系统中的多个模块交互,而且客户端也不需要知道那么多模块的细节功能了,实现这个功能的就是Facade。

外观模式的目的不是给子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单的使用子系统。

外观应该是包装已有的功能,它主要负责组合已有功能来实现客户需要,而不是添加新的实现

应用场景:

  用户注册完之后,需要调用短信接口,微信接口,邮件接口。只提供一个接口发消息接口,隐藏内部复杂实现。

 装饰器模式(Decorator):

装饰器模式作用是针对目标方法进行增强,提供新的功能或者额外的功能。

不同于适配器模式和桥接模式,装饰器模式涉及的是单方,和代理模式相同,而且目标必须是抽象的。

所谓单方主要指的是在整个装饰器模式中不存在双方调用,要解决的也不是双方调用的问题,而是解决单方提供对外服务的问题,这个单方在自行对外提供服务时,功能不足,或者我们需要额外添加一些新功能,这时就可以使用装饰器模式,来对这个单方进行增强。

目标抽象的意思是因为我们需要通过实现接口的方式来进行增强,因此目标必须抽象为接口。

代理和装饰的区别:

代理是全权代理,目标根本不对外,全部由代理类来完成。

装饰是增强,是辅助,目标仍然可以自行对外提供服务,装饰器只起增强作用。

装饰器中持有的目标实例是从构造器传入的,而代理中持有的目标实例是自己创建的。

 

应用场景:

装饰器模式就是使用在对已有的目标功能存在不足,需要增强时,前提是目标存在抽象接口。

------ 行为型 ------

模板方法模式(TemplateMethod)

定义一个操作中的算法骨架,而将一些步骤延迟到子类实现。重复代码全部在父类里面,不同业务的,抽取给子类进行实现。处理步骤在父类中定义好,具体的实现延迟到子类中定义。

应用场景:银行办业务。银行给我们提供了一个模板就是:先取号,排对,办理业务(核心部分我们子类完成),给客服人员评分,完毕。

Servlet的doGet/doPost方法,Hibernate的模板程序,Spring中JDBCTemplate, RestTemplate等。

策略模式(Stragey):

针对一组算法或逻辑,将每一个算法或逻辑封装到具有共同接口的独立的类中,从而使得它们之间可以相互替换。策略模式使得算法或逻辑可以在不影响到客户端的情况下发生变化。OCP开闭原则。

应用场景:

不同等级会员打折力度不同,分为三种策略,初级会员,中级会员,高级会员。

观察者模式(Observer):

主要用于1对N的通知。当一个对象的状态变化时,他需要及时告知一系列对象,令他们做出相应。

两种实现方式:

推:把通知以广播的方式发送给所有观察者,所有的观察者只能被动接收。

拉:观察者只要知道有情况即可,至于什么时候获取内容,获取什么内容,都可以自主决定。

应用场景:

跨系统消息交换场景,消息队列,事件监听处理。

原文地址:https://www.cnblogs.com/fyql/p/11796552.html