设计模式笔记

二十三种设计模式

三种类型

创建型模式:

​ 单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式;

结构型模式:

​ 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式;

行为型模式:

​ 模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter)模式、状态模式、策略模式、职责链模式(责任模式) ;

设计模式

一、单例模式

​ 采用一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类值提供一个取的其对象实例的方法(静态方法),一共八种方式。

使用场景和细节说明

​ 1)单利模式保证系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁对象,使用单利模式可以提高系统性能;

​ 2)当想实例化一个单例类时必须使用相应的获取对象的方法,而不是使用new;

​ 3)使用场景:需要频繁的进行创建和销毁的对象、创建对象耗时过多或耗费资源过多(即:重量级对象),但又经常使用的对象、工具类对象、频繁访问数据库或者文件的对象(比如数据源,session工厂等)

1、饿汉式(静态变量)

​ 例:JDK Runtime

步骤:1)构造器私有化 2)类的内部创建对象 3)向外提供一个静态的公共方法

优缺点:

​ 优点:写法简单,在类装载的时候完成实例化。避免了线程同步问题;

​ 缺点:在类装载的时候就完成实例化。没有达到Lazy Loading(满加载)得效果。如果从始至终都没有使用该实例,会造成内存浪费;

​ 结论:可用,可能造成内存浪费。

//饿汉式  (静态变量) 初始化类未调用就加载
public class SingletonOne {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);  //ture
        //singleton.hashCode() = singleton1.hashCode()
        System.out.println("Singleton HashCode = "+singleton.hashCode());
        System.out.println("Singleton HashCode1 = "+singleton1.hashCode());
    }
}

class Singleton{
    //私有化构造方法
    private Singleton(){}
    //通过静态常量创建实例
    private final static Singleton instance = new Singleton();
    //获取实例
    public static Singleton getInstance(){ return instance; }
}
2、饿汉式(静态代码块)

优缺点:

​ 与静态常量一致

//饿汉式  (静态代码块) 初始化类未调用就加载
public class SingletonTwo {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);  //ture
        //singleton.hashCode() = singleton1.hashCode()
        System.out.println("Singleton HashCode = "+singleton.hashCode());
        System.out.println("Singleton HashCode1 = "+singleton1.hashCode());
    }
}

class Singleton{
    //私有化构造方法
    private Singleton(){}
    //私有化静态变量
    private static Singleton instance ;
    //通过静态代码块创建实例
    static{
       instance = new Singleton();
    }
    //获取实例
    public static Singleton getInstance(){ return instance; }
}
3、懒汉式(线程不安全)

优缺点

​ 优点:起到了Lazy Loading 效果;

​ 缺点:只能在单线程下使用。如果在多线程下,一个线程进入了if(singleton == null)判断语句块,还未往下执行,另一个线程也通过这个判断语句进入,就会产生多个实例;

​ 结论:在实际开发不适用。

//懒汉式(非线程安全)
public class SingletonThree {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);  //ture
        //singleton.hashCode() = singleton1.hashCode()
        System.out.println("Singleton HashCode = "+singleton.hashCode());
        System.out.println("Singleton HashCode1 = "+singleton1.hashCode());
    }
}

class Singleton{
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}
4、懒汉式(线程安全,同步方法)

优缺点

​ 优点:解决线程不安全问题;

​ 缺点:效率低,每个线程想要获取类的实例的时候,执行getINstance()方法都要进行同步。而这个方法其实只需要执行一次,后面想要获得该实例直接return就行。

​ 结论:实际开发中不推荐使用。

//懒汉式(线程安全,同步方法)
public class SingletonFour {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);  //ture
        //singleton.hashCode() = singleton1.hashCode()
        System.out.println("Singleton HashCode = "+singleton.hashCode());
        System.out.println("Singleton HashCode1 = "+singleton1.hashCode());
    }
}

class Singleton{
    private static Singleton instance;
    private Singleton(){}
    //加入同步处理代码,解决线程安全
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}
5、懒汉式(线程安全,同步代码块)

​ 优缺点:同 懒汉式(非线程安全)本意是想改进同步方法效率低下问题,但这种同步起不到线程安全作用,也是可能多个方法同时进入if判断。

//懒汉式(线程安全,同步代码块)
public class SingletonFive {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);  //ture
        //singleton.hashCode() = singleton1.hashCode()
        System.out.println("Singleton HashCode = "+singleton.hashCode());
        System.out.println("Singleton HashCode1 = "+singleton1.hashCode());
    }
}

class Singleton{
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                instance = new Singleton();
            }
        }
        return instance;
    }
}  
6、双重检查

优缺点

​ 优点:Double-Check是多线程开发中经常使用到的,进行两次if检查,就可以保证线程安全;实例代码只用执行一次,后面再次访问时,判断if直接返回return实例化对象,能直接避免反复进行同步方法;延迟加载,效率较高;

​ 结论:实际开发中推荐使用这种单例模式。

//双重检查  (线程安全,效率高)
public class SingletonSix {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);  //ture
        //singleton.hashCode() = singleton1.hashCode()
        System.out.println("Singleton HashCode = "+singleton.hashCode());
        System.out.println("Singleton HashCode1 = "+singleton1.hashCode());
    }
}

class Singleton{
    private static volatile Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
7、静态内部类

优点:

​ 1)这种方式采用类装载机制来保证初始化实例时只有一个线程;

​ 2)静态内部类的在Singleton类被装载时不会被立即实例化,而是需要实例化时,调用getInstance方法才会装载SingletonInstance类,从而完成Singleton实例;

​ 3)类的静态属性只会在第一次加载类的时候初始化,所以这里JVM 帮助我们保证了线程的安全性,在类进行初始化时,别的线程时无法进入的;

​ 4)避免线程不安全,利用静态内部类特点实现延迟加载,效率高。

//静态内部类
public class SingletonEight {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);  //ture
        //singleton.hashCode() = singleton1.hashCode()
        System.out.println("Singleton HashCode = "+singleton.hashCode());
        System.out.println("Singleton HashCode1 = "+singleton1.hashCode());
    }
}

class Singleton{
    private static Singleton instance;
    private Singleton (){}
    private static class SingletonInstance{
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance(){ 
        return SingletonInstance.INSTANCE;
    }
}
8、枚举

优点:

​ 避免多线程同步问题,还能防止反序列化重新创建新的对象。推荐使用

//枚举
public class SingletonEight {
    public static void main(String[] args) {
        Singleton singleton = Singleton.INSTANCE;
        Singleton singleton1 = Singleton.INSTANCE;
        System.out.println(singleton == singleton1);  //ture
        //singleton.hashCode() = singleton1.hashCode()
        System.out.println("Singleton HashCode = "+singleton.hashCode());
        System.out.println("Singleton HashCode1 = "+singleton1.hashCode());
    }
}

enum Singleton{
    INSTANCE; //属性
}
二、工厂方法模式

1)意义:将实例化对象的代码提取出来,放到一类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。

2)三种工厂模式:简单工程模式、工厂方法模式、抽象工厂模式;

3)设计模式的依赖抽象原则:

​ 1、创建对象实例时,不要直接new类,而是把这个new类的动作放在一个工厂的方法中,并返回。变量不要直接持有具体类的引用。

​ 2、不要让类继承具体类,而是继承抽象类或者实现interface。

​ 3、不要覆盖类中已实现方法。

1、简单工厂模式(静态工厂模式)

例:JDK Calendar

1)简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是有一个工厂对象决定创建出哪一种产品类型的实例。简单工程模式是工厂模式家族中最简单使实用的模式。

2)简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为。

3)在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式。

2、工厂方法模式

定义了一个创建对象的抽象方法,有子类决定要实例化的类,工厂方法模式将对象的实例化推迟到子类。

3、抽象工厂模式

​ 1)抽象工厂模式:定义了一个interface用于创建相关或者有依赖关系的对象簇,而无需指明具体的类。

​ 2)抽象工厂模式可将简单工厂模式和工厂方法模式进行整合。

​ 3)从设计层面看,抽象工厂模式就是对简单工厂模式的改进。

​ 4)将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

三、原型模式

​ 1)圆形模式指:用原型实例指定创建对象的种类,并通过拷贝这些原型,创建新的对象;

​ 2)原型模式是一种创建型设计模式,允许一个对象再创建另一个可定制的对象,无需知道如何创建的细节;

​ 3)工作原理:通过将一个原型对象传给那个需要发动创建的对象,这个要发动常见的对象通过请求原型对象拷贝他们自己来实施创建,即对象.clone()。

注意事项和细节

​ 1)创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率;

​ 2)不用重新初始化对象,而是动态地获取对象运行时的状态;

​ 3)如果原始对象发生变化(增加或者减少属性),其他克隆对象也会发生相应的标号,无需修改代码;

​ 4)在实现深克隆的时候可能需要比较复杂的代码

​ 5)缺点:需要为每一个类配备一个克隆方法,这对全新的类说不是很难,但对已有的类进行改造时,需要修改器源代码,违背了ocp原则。

方式一:类实现Cloneable方法,重写Object中的clone方法。使用该方法为浅拷贝,类中的对象指向同一内存地址。

例:Spring中原型bean的创建。

浅拷贝

​ 1)对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将属性的值复制一份给新的对象;

​ 2)对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只将该成员变量的引用值(内存地址)复制一份给新的对象。实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响另一个对象的该成员变量。

深拷贝

​ 1)复制对象的所有基本数据类型的成员变量值;

​ 2)为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型的成员变量所引用的对象,直到该对象可达的所有对象,也就是说,对象进行深拷贝要对整个对象进行拷贝。

**深拷贝方式:**1、重写clone方法来实现深拷贝;2、通过对象序列化实现深拷贝。

四、建造者模式

​ 1)建造者模式又叫生成器模式,是一种对象构建模式。他可以将复杂对象的的建造过程抽象出来,是这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。

​ 2)建造者模式是一步一步创建一个复杂的对象,他允许用户只通过指定负责对象的类型和内容就可以构建他们,用户不需要指导内部的具体苟新建细节。

例:StringBuilder

建造者模式的四个角色:

​ 1)Product(产品角色):一个具体的产品对象;

​ 2)Builder(抽象建造者):创建一个Product对象的各个部件指定的接口;

​ 3)ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件;

​ 4)Director(指挥者):构建一个使用Builder接口的对象,他主要用于创建一个复杂的对象。他主要有两个作用,一是:隔离了客户与对象的生产过程;而是:负责控制产品对象的生产过程。

注意事项和细节

​ 1)客户端不必知道产品内部组成细节,将产品本身与产品的创建过程解耦,使得相同创建过程可以创建不同的产品对象;

​ 2)每一个具体建造者都是相互独立,而与其他的具体建造者无关,用户使用不同的具体建造者即可得到不同的产品对象;

​ 3)可以更加精细地控制产品的创建过程;

​ 4)增加新的具体建造者无须修改原有类库的代码。指挥者类针对抽象建造者编程,系统扩展方便,符合“开闭原则“;

​ 5)建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性较大,则不适合使用建造者模式,因此其使用范围收到一定的限制;

​ 6)如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式;

​ 7)抽象工厂VS建造者模式

​ 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品;具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品有什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,他的主要目的是通过组装零件二产生一个新产品。

五、适配器模式

​ 1)适配器模式将某个类的接口转换成客户端期望的另一个接口表示,主要的目的是兼容键,让原本因接口不匹配的不能在一起工作的两个类可以协同工作。其别名为包装器;

​ 2)适配器模式属于结构型模式;

​ 3)主要分为三类:类适配器模式、对象适配器模式、接口适配器模式。

例:SpringMVC中HandleAdapter

1、类适配器

工作原理

​ 1)适配器模式:将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容;

​ 2)从客户的角度看不到适配者,是解耦的;

​ 3)用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法;

​ 4)用户收到反馈结果,感觉只是和目标接口交互,如图:

基本介绍:Adapter类,通过继承src类,实现dst类接口,完成ser->dst的适配。

注意事项和细节

​ 1)java是单继承机制,所以类适配器需要继承src类算是一个缺点,因为要求dst必须是接口,有一定局限性;

​ 2)src类的方法在Adapter中都暴露出来,也增加了使用成本;

​ 3)由于继承了src类,所以他可以根据需求重写src类的方法,使得Adapter的灵活性更强;

2、对象适配器

​ 1)基本思路和类的适配器模式相同,只是将Adapter类作修改,而不是继承src类,而是持有src类的实例,已解决兼容性问题。即:持有src类,实现dst类接口,完成src->dst的适配;

​ 2)根据:“合成复用原则”,在系统中尽量使用关联关系来替代继承关系;

​ 3)对象适配器模式是适配器模式常用的一种。

注意事项和细节

​ 1)对象适配器和类适配器算是一种思想,,实现方法不同;根据合成复用原则,使用组合替代集成,所以他解决了类适配器必须继承src的局限性问题,也不再要求dst必须是接口;

​ 2)使用高成本低,更灵活。

3、接口适配器模式

​ 1)适配器模式又叫缺省适配器模式;

​ 2)当不需要实现全部接口提供的方法时,可先设计一个抽象类实现接口,并为改接口中每个方法提供一个默认实现,那么该抽象类的子类可以有选择地覆盖父类的某些方法来实现;

​ 3)适用于一个接口不想使用其他所有的方法的情况。

六、桥接模式

​ 1)桥接模式:将实现与抽象放在两个不同类的层次中,是两个层次可以独立改变;

​ 2)是一种结构性设计模式;

​ 3)Bridge模式居于类的最小设计原则,通过使用封装、聚合以及继承等行为让不同的类承担不同的职责。他的主要特点是把抽象与行为实现分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展。

例:JDBC连接

注意事项与细节

​ 1)实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分分离开来,这有助于系统进行分层设计,从而生产更好的结构化系统;

​ 2)对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其他的部分有具体业务来实现;

​ 3)桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本;

​ 4)桥接模式和引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程;

​ 5)桥接模式要求正确识别出系统中两个独立变化的维度,因此其适用范围有一定的局限性,及需要这样的应用场景。

常见场景

​ 1)对于那些不希望使用继承或者多层继承导致系统类的个数急剧增加的系统,桥接模式尤为合适;

​ 2)常见的应用场景:

	- JDBC启动程序;
  • 银行转账系统

    • 转账分类:网上转正、柜台转账、AMT转账;
    • 转账用户类型:普通用户、银卡用户、金卡用户;
  • 消息管理

    • 消息类型:即时消息、延时消息;
    • 消息分类:手机短信、邮件消息、QQ消息,。。。
七、装饰者模式

​ 装饰者模式:动态得将新功能附加到对象上。在对象功能扩展方面,他比继承更有弹性,装饰者模式也体现了开闭原则。

例:java的IO结构,FilterInputStream;

八、组合模式

​ 1)组合模式又叫部分整体模式,他创建了对象组的树形结构,将对象组合成树状接结构以表示“整体-部分”的层级关系;

​ 2)组合模式依据树形结构来组合对象,用于表示部分以及整体层次;

​ 3)这种类型的设计模式属于结构型模式;

​ 4)组合模式使得用户对单个对象和组合对象访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象。

应用场景:

​ 1)当要处理的对象可以生成一棵树形结构,而我们要把树上的节点和叶子节点进行操作时,它能够提供一致的方式,而不考虑它是节点还是叶子。

​ 2)对应的示意图:

例:java集合HashMap

注意事项和细节

​ 1)简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题;

​ 2)具有较强的扩展性,当我们要更改组合对象时,我们只需要调整内部的层级关系,客户端不进行任何修改;

​ 3)方便创建出比较复杂的层级结构。客户端不用管理会组合里面的组合细节,容易添加节点或者叶子从而创建出复杂的树形结构;

​ 4)需要遍历组织机构,或者吃力的对象具有树形结构时非常适合使用组合模式;

​ 5)要求较高的抽象,如何节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式。

九、外观模式

​ 1)外观模式也叫过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用;

​ 2)外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需要跟这个接口发生调用,而无需关系这个子系统的内部细节。

例:Mybatis中Configuration中创建MetaObject对象使用到外观模式;

注意事项和细节

​ 1)外观模式对屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性;

​ 2)外观模式对客户端与子系统的耦合关系,让子系统内部的模块更容易维护和扩展;

​ 3)通过合理的使用外观模式,可以帮我们更好的划分访问的层次;

​ 4)当系统需要进行分层设计时,可以考虑使用Facade模式;

​ 5)在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的与接口,让新系统与Facade类交互,提高复用性;

​ 6)不能过多地或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。

十、享元模式

​ 1)享元模式也叫蝇量模式:运用共享 技术有效的支持大量细粒度的对象;

​ 2)常用语系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个;

​ 3)享元模式能解决对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时,不需要总是创建对象,可以从缓冲池中拿,这样可以降低系统内存,同时提高效率;

​ 4)享元模式讲点应用场景就是池技术,String常量池、数据库连接池、缓冲池等技术都是享元模式的应用,享元模式是池技术的重要实现方式。

内部状态和外包部状态

​ 1)享元模式提出的两个要求:细粒度和共享对象。也就是内部状态和外部状态;

​ 2)内部状态:对象共享出来的信息,存储在享元对象内部且不会随着环境的变化而变化;

​ 3)外部对象:对象得以依赖的一个标记,是随着环境变化而变化、不可共享的状态;

例:java中Integer

注意事项和细节

​ 1)系统有大量对象,这些对象创建消耗大量内存,并且对象的状态大部分可以外部化时,可以考虑使用享元模式;

​ 2)用唯一标识判断,如果内存中有,则返回这个唯一标识码标识的对象,用HashMap\HashTable存储;

​ 3)享元模式大大减少了对象的创建,降低了程序中内存的占用,提高效率;

​ 4)享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特征,不应该随着内部状态改变而改变。

​ 5)使用享元模式时,注意划分内部状态和外部状态,并且需要一个工厂类加以控制;

十一、代码模式

​ 1)代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象,即扩展目标对象的功能;

​ 2)被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象;

​ 3)代理模式有不同的形式,主要有三种 静态代理、动态代理(JDK代理、接口代理)和Cglib代理(可以在动内存动态的创建对象,而不需要实现接口,它是属于动态代理的范畴)。

1、静态代理

​ 静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同的父类。

优缺点

​ 优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展;

​ 缺点;因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类;一旦接口增加方法,目标对象与代理对象都要维护。

2、动态代理

​ 1)代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理;

​ 2)代理对象的生产,是利用JDK的API,动态的在内存中构建代理对象;

​ 3)动态代理也JDK代理、接口代理。

Cglib代理

​ 1)静态代理和动态代理模式都要求目标对象是实现一个接口,但是有时候目标对象值是一个独立的对象,并没有实现任何接口,这时候就可使用目标对象子类来实现代理,这就是Cglib代理;

​ 2)Cglib代理也叫做子类代理,他是在内存中构建一个子类对象从而实现对目标对象功能扩展;

​ 3)Cglib是一个强大的高性能的代码生成包,他可以在运行期间扩招java类与实现java接口,它广泛的被许多AOP框架使用,例如SpringAop,实现方法拦截;

4)在AOP编程中如何使用代理模式:

​ (1)目标对象需要实现接口,用JDK代理;

​ (2)目标对象不需要实现接口,用Cglib代理;

5)Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。

Cglib代理模式实现步骤

​ 1、需要引入cglib的jar包;

​ 2、在内存中动态构建子类,需要代理的类不是final,否则报错java.lang.IllegalArgumentExecption;

​ 3、目标对象方法如果为final/static,那么就不会被拦截,及不会执行目标对象额外的业务方法;

代理模式的变体

1)防火墙代理

​ 内网通过代理穿透防火墙,实现对公网的访问;

2)缓存代理

​ 当请求图片文件资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网或者数据库取,然后缓存;

3)远程代理

​ 远程对象的本地代理,通过它可以把远程对象当做本地对象调用。远程代理通过网络和真正的远程对象沟通信息;

4)同步代理

​ 主要使用在多线程编程中,完成多线程间同步工作。

十二、模板模式

​ 1)模板方法,又叫末班模式,在一个抽象类公开定义了执行他的方法的模板,他的子类可以按需要重写方法实现,但嗲用将以抽象类中定义的方式进行;

​ 2)模板方法模式定义一个操作中的算法的股价,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重新定义该算法的某些特定步骤;

​ 3)这种类型的设计模式属于行为型模式。

例:Spring 中IOC

模板模式钩子方法

​ 1)在模板方法模式的父类中,我们可以定义一个方法,他可以默认不做任何事,子类可以视情况要不要覆盖他,该方法称为钩子;

注意事项和细节

​ 1)基本思想:算法只存放在与一个地方,也就是父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某种步骤,子类就会集成这些修改。

​ 2)实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用;

​ 3)既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的机构保持不变,同时由子类提供部分步骤的实现;

​ 4)该模式的不足:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大;

​ 5)一般模板方法都加上final关键字,防止子类重写模板方法;

​ 6)模板方法的使用场景:当要完成在某个过程,该过程要执行一系列步骤,这一系列的步骤基本相同,但其个别步骤在实现时可能不同,通常考虑使用模板方法模式处理;

十三、命令模式

​ 1)命令模式:在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作时哪个;我们只需要在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来设计;

​ 2)命令模式使得请求发送者与请求接收者消除彼此之际的耦合,让对象之间的调用关系更加灵活,实现解耦;

​ 3)在命令模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求,同时命令模式也支持撤销操作;

例:jdbcTemplate

十四、访问者模式

​ 1)访问者模式,封装一些作用于某种数据结构的各元素的操作,他主要在不改变数据结构的前提下定义作用于这些元素的新的操作;

​ 2)主要将数据结构与数据操作分离,结局数据结构和操作耦合性问题;

​ 3)访问者模式的基本工作原理:在被访问的类里加一个对外提供接待访问者的接口;

​ 4)访问者模式主要应用场景:需要对一个对象结构中的对象进行很多不同的操作(这些操作彼此之间没有关联),同时需要避免这些操作污染这些对象的类,可以选用访问者模式解决。

注意事项与细节

优点:

​ 1)访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高;

​ 2)访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统;

缺点:

​ 1)具体元素对访问者公布细节,也就是或访问者关注了其他类的内部细节,这是迪米特法则不建议的,这样造成具体元素变更比较困难;

​ 2)违背了依赖倒转原则。访问者依赖的是具体对象,而不是抽象元素;

​ 3)如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就比较合适;

十五、迭代器模式

​ 1)迭代器模式是常用的设计模式,属于行为型模式;

​ 2)如果我们的结合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且会暴露元素的内部结构,可以考虑使用迭代器模式解决。

​ 3)迭代器模式,提供一种遍历结合元素的统一接口,用一致的方法遍历集合元素,不需要指导集合对象的底层表示,即:不暴露其内部结构。

例:JDK 的ArrayList

说明:

​ (1)Iterator:迭代器结构,是系统提供的,包含hasNext,nextremove;

​ (2)ConcreteIterator:具体的迭代器类,管理迭代;

​ (3)Aggregate:一个统一的聚合接口,将客户端和具体聚合结构;

​ (4)ConcreateAfggreage:具体的聚合持有对象集合,提供一个 方法,返回一个迭代器,该迭代器可以正确遍历结合;

​ (5)Client:客户端,通过Iterator和Aggreage依赖子类;

学院、专业实例UML类图:

注意事项和细节

优点:

​ 1)提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象;

​ 2)隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取代到迭代器,而不会指导聚合的具体组成;

​ 3)提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一职责原则),在聚合类中,我们把迭代器分开,就是要把管理对象聚合和遍历对象聚合的责任分开,这样一来集合改变的话,只影响到聚合对象,而如果遍历方式变的话,只影响到迭代器;

​ 4)要展示一组相似对象,或者遍历一组相同对象时使用,适合使用迭代器模式;

缺点:

​ 每个集合对象都要一个迭代器,会生成多个迭代器不好管理类。

十六、观察者模式

​ 1)观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,Subject通知Observer拜年话。

例:JDK的Observable

十七、中介者模式

​ 1)中介者模式:用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显示的互相引用,从而使其耦合松散,而且可以独立改变他们之间的交互;

​ 2)中介者模式属于行为模式,使代码易于维护;

​ 3)比如MVC模式,C(Controller控制器)是M(Model模型)和V(View视图)的中介者,在前后端交互时起到了中间人的作用。

注意事项和细节

​ 1)多个类相互耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构,进行解耦;

​ 2)减少类之间依赖,降低耦合,符合迪米特法则;

​ 3)中介者承担了较多的责任,一旦中介者出现了问题,整个系统过就会受到影响;

​ 4)如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时要特别注意。

十八、备忘录模式

​ 1)备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保持这个状态。这样以后就可以将该对象恢复到原先保存的状态;

​ 2)备忘录对象永安里记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里回去到原来的数据进行恢复操作;

​ 3)备忘录模式属于行为模式;

注意事项和细节

​ 1)给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回某个历史状态;

​ 2)实现了信息的封装,使得用户不用关系状态的保存细节;

​ 3)如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存;

​ 4)使用的场景:1、后悔药;2、打游戏时的存档;3、Windows里 的ctri+z;4、IE中的后腿;5、数据库的事务管理;

​ 5)为了节约内存,备忘录模式可以和原型模式配合使用。

十九、解释器模式

​ 1)解释器模式:指定一个语言,定义他的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子;

​ 2)应用场景:

​ (1)应用可以将一个需要解释执行的语句中的句子表示为一个抽象语法树;

​ (2)一些重复出现的问题可以用一种简单的居于来表达;

​ (3)一个简单语法需要解释的场景;

注意事项和细节

​ 1)当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展;

​ 2)应用场景:编译器、运算表达式、机器人等;

​ 3)使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归方式,将会导致调试非常复杂,效率可能降低;

二十、状态模式

​ 1)状态模式:主要用来解决对象再多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换;

​ 2)当一个对象的内在状态改变是,允许改变其姓改为,这个对象看起来像是改变了其类;

注意事项和细节

​ 1)代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中;

​ 2)方便维护。将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,补单会产生很if-else语句,而且容易出错;

​ 3)符合开闭原则,容易增删状态;

​ 4)会产生很多类。每个状态都要一个对应的类,当状态过多时产生很多类,加大维护难度;

​ 5)当一个事件或者对象有很多种状态,状态之间会相互转换,对不用的状态要求有不同的行为时,可以考虑使用状态模式;

二十一、策略模式

​ 1)策略模式中,定义算法族,分别封装起来,让他们之间可以相互替换,此模式 让算法的变化独立于使用算法的客户;

​ 2)这种算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二:针对接口编程而不是具体的类(定义了策略接口);第三、都用组合\聚合少用继承(客户通过组合方式使用策略)

注意事项和细节

​ 1)策略模式的关键是:分析项目中变化部分与不变部分;

​ 2)策略模式的核心思想是:多用组合\聚合,少用继承;用性行为类组合,而不是行为的继承,更有弹性;

​ 3)体现了“对修改关闭,对扩张开放"原则,客户端增加行为不用修改原有代码,只要添加一种策略即可,避免使用多重转移语句(if…else)

​ 4)提供了可以替换继承关系的办法:策略模式将算法封装在独立的Strategy类中使得可以独立于Context改变他,使他易于切换、易于理解、易于扩展;

​ 5)需要注意的是:每添加一个策略就要增加一个类,当策略过多时会导致类数目庞大;

二十二、责任链模式

​ 1)责任链模式:为请求创建一个接收者对象的链。这种模式对请求的发送者和接收者进行解耦;

​ 2)责任链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么他会把相同的请求传给下一个接收者,一此类推;

​ 3)这种类型的设计模式属于行为模式;、

注意事项和细节

​ 1)将请求和处理分开,实现解耦,提高系统的灵活性;

​ 2)简化了对象,使对象不需要知道链的结构;

​ 3)性能会受到影响,特别是在链比较长的时候,因此需要控制链中最大节点的数量,一般通过在Handler中设置一个最大节点数量,在setNext方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能;

​ 4)调试不方便,采用了类似递归的方式,调试时逻辑可能比较复杂;

​ 5)最佳应用场景;有多个对象可以处理同一个请求,比如:多级请求、请假/加薪等审批流程、Java Web中Tomcat对Encoding的处理、拦截器;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值