Java设计模式

Posted on 2021-03-12 22:29  金色的省略号  阅读(775)  评论(0编辑  收藏  举报

    第一部分  设计模式的概念、目的、原则

  一、设计模式(design pattern)

    设计模式,是对软件设计中普遍存在(反复出现)的各种问题所提出的解决方案是九十年代从建筑设计领域引入到计算机科学的

  二、设计模式的目的

    设计模式的目的,是为了让程序(软件)具有更好的代码重用性、可读性、可扩展性、可靠性、使程序呈现高内聚、低耦合的特性

  三、设计模式遵循的七大原则

    设计原则核心思想: 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起;针对接口编程,而不是针对实现编程;为了交互对象之间的松耦合设计而努力

    1、单一职责原则可以是类级别的,一个类只负责一项职责,可以是方法级别的,一个方法负责一项职责,只有类中方法数量足够少,可以在方法基本保持单一职责原则

    2、接口隔离原则,接口复杂的可以拆分成多个不同的接口,类分别与它们需要的接口建立依赖关系

    3、依赖倒置原则(Dependence Inversion Principle),高层模块不应该依赖底层模块,二者应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象;中心思想是面向接口编程

package principle.inversion;

public class DependecyInversion {
    public static void main(String[] args) {
        Person person = new Person();

        person.recieve(new Email());
        person.recieve(new Wenxin());
    }
}

interface IRecieve { // 接口
    public String getInfo();
}

class Email implements IRecieve { // 实现接口
    @Override
    public String getInfo() {
        return "电子邮件信息:Hello World";
    }
}

class Wenxin implements IRecieve { // 实现接口

    @Override
    public String getInfo() {
        return "微信信息:Hello";
    }
}

class Person {
    public void recieve(IRecieve ir) { // 依赖接口
        System.out.println(ir.getInfo());
    }
}
View Code 

    4、里氏替换原则,继承在给程序带来便利的同时,也带来弊端,带来了侵入性,所有引用基类的地方必须能透明地使用其子类的对象,在子类中尽量不要重写父类的方法,可以通过聚合、组合、依赖来解决问题,让原来的父类和子类都继承一个更通俗的基类,原来的继承关系去掉,采取聚合、组合等关系代替

package principle.liskov;

public class Liskov {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        A a = new A();
        System.out.println("11-3=" + a.fun1(11, 3));
        System.out.println("===================");
        B b = new B();
        System.out.println("8+3=" + b.fun1(8, 3));
        System.out.println("1+3+9=" + b.fun2(1, 3));
        System.out.println("1-8=" + b.fun3(1, 8));
    }
}

class Base { // A、B的基类

}

class A extends Base { // A继承更基础的基类
    public int fun1(int a, int b) {
        return a - b;
    }
}

class B extends Base { // B继承更基础的基类
    A a = new A(); // 组合A对象

    public int fun1(int a, int b) { // 不会混淆A、B的方法fun1(),去掉了A、B原有的继承关系
        return a + b;
    }

    public int fun2(int a, int b) {
        return fun1(a, b) + 9;

    }

    public int fun3(int a, int b) { // 利用组合的A对象,使用A方法
        return this.a.fun1(a, b);
    }
}
View Code

    5、开闭原则,一个软件实体,模块和函数应该扩展开放(提供方:提供模块和函数的),对修改关闭(使用方:使用模块和函数的),用抽象构建框架,用实现扩展细节,当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化

package principle.ocp;

/**
 * 开闭原则,Ocp (Open Closed Principle)
 *
 */
public class Ocp {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());
        //如果扩展新的图形类(比如矩形类),不需要修改使用方 GraphicEditor
        //即扩展开放,修改闭合
    }
}

/**
 * 使用方
 * 扩展新的图形类,不需要修改使用方代码
 */
class GraphicEditor{
    //接受Shape绘制不同图形
    public void drawShape(Shape s) {
        s.draw();
    }
}

/**
 * 提供方
 * 抽象类,Shape类,基类
 */
abstract class Shape{
    int m_type;
    public abstract void draw(); //抽象方法
}

//圆形类,继承自Shape
class Circle extends Shape{
    public Circle() {
        // TODO Auto-generated constructor stub
        super.m_type = 1;
    }
    @Override
    public void draw() {
        // TODO Auto-generated method stub
        System.out.println("绘制圆形");
    }
    
}

//三角形类,继承自Shape
class Triangle extends Shape{
    public Triangle() {
        // TODO Auto-generated constructor stub
        super.m_type = 2;
    }
    @Override
    public void draw() {
        // TODO Auto-generated method stub
        System.out.println("绘制三角形");
    }
}
View Code

    6、迪米特法则,Demeter Principle,又叫最少知道原则,只与直接的朋友通信:每个对象都会与其他对象有耦合关系(耦合方式:依赖,关联,组合,聚合),其中出现在成员变量,方法参数,方法返回值的类为直接朋友出现在局部变量中的类不是直接朋友陌生的类不要以局部变量的形式出现在类的内部

    7、合成复用原则尽量使用合成/聚合的方式,而不是使用继承    

 

    第二部分  23种设计模式之模式释义

      设计模式分三种类型,共23种:   

  一、创建型模式

  1、单例模式

  单例模式只创建一个类的实例(类对象)

    1) 保证线程安全,保证懒加载的方法之一双重检查

public class SingletonTest06 {

    public static void main(String[] args) {
        System.out.println("双重检查");
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
        
    }

}

// 懒汉式(线程安全,同步方法)
class Singleton {
    private static volatile Singleton instance;
    
    private Singleton() {}
    
    //提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题
    //同时保证了效率, 推荐使用
    
    public static synchronized Singleton getInstance() {
        if(instance == null) {
            synchronized (Singleton.class) {
                if(instance == null) {
                    instance = new Singleton();
                }
            }
            
        }
        return instance;
    }
}
View Code

    2) 保证线程安全,保证懒加载的方法之一静态内部类

public class SingletonTest07 {

    public static void main(String[] args) {
        System.out.println("使用静态内部类完成单例模式");
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
        
    }

}

// 静态内部类完成, 推荐使用
class Singleton {
    private static volatile Singleton instance;
    
    //构造器私有化
    private Singleton() {}
    
    //写一个静态内部类,该类中有一个静态属性 Singleton
    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton(); 
    }
    
    //提供一个静态的公有方法,直接返回SingletonInstance.INSTANCE
    
    public static synchronized Singleton getInstance() {
        
        return SingletonInstance.INSTANCE;
    }
}
View Code

    3) 保证线程安全,保证懒加载的方法之一枚举类

public class SingletonTest08 {
    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance == instance2);
        
        System.out.println(instance.hashCode());
        System.out.println(instance2.hashCode());
        
        instance.sayOK();
    }
}

//使用枚举,可以实现单例, 推荐
enum Singleton {
    INSTANCE; //属性
    public void sayOK() {
        System.out.println("ok~");
    }
}
View Code

    4) 保证线程安全,保证懒加载的方法之一饿汉模式

public class SingletonTest01 {

    public static void main(String[] args) {
        //测试
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
    }

}

//饿汉式(静态变量)

class Singleton {
    
    //1. 构造器私有化, 外部能new
    private Singleton() {
        
    }
    
    //2.本类内部创建对象实例
    private final static Singleton instance = new Singleton();
    
    //3. 提供一个公有的静态方法,返回实例对象
    public static Singleton getInstance() {
        return instance;
    }
    
}
View Code

  单例模式保证了系统内存中只存在一个对象,使用场景为:需要频繁创建销毁的对象创建对象耗时或耗资源,但又经常用到的对象工具类对象频繁访问数据库或文件的对象

  2、抽象工厂模式

  抽象工厂模式,AbstractFactory 模式是为创建一组( 有多类) 相关或依赖的对象提供创建接口

 

  3、原型模式

  原型模式原型模式其实就是从一个对象再创建另外一个可定制的对象而且不需要知道任何创建的细节,Java提供了Cloneable接口,实现了这接口 ( @Override clone() )就可以完成原型模式了

 

  4、建造者模式

  建造者模式(Builder)将一个复杂对象的构建,与它的表示分离,使得同样的构建过程,可以创建不同的表示 ,指挥者(Director),控制构建过程,用来隔离用户与构建过程的关联,建造者模式,主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化,建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了

  5、工厂模式

  工厂模式 ,也叫工厂方法模式,简单工厂模式是工厂方法模式的特例, Factory 模式是为创建一类对象提供创建接口或延迟对象的创建到子类中实现 

  二、结构型模式

  6、类适配器模式

  类适配器模式Adapter类,通过继承scr类,实现dst类接口,完成src->dst的适配

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

  接口适配器模式,当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类(可采取匿名内部类)可有选择地覆盖父类的某些方法来实现需求

  7、桥接模式

  桥接模式(Bridge),将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变,通过使用封装、聚合及继承等行为让不同的类承担不同的职责,抽象聚合实现,抽象类维护实现类,抽象类充当桥接类

  8、装饰模式

  装饰模式,动态的将新功能附加到对象上,体现了开闭原则,装饰者模式类似于打包一个快递,层层包裹一个对象

  9、组合模式

  组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构将对象组合成树状结构,以表示整体部分的层次关系,对单个对象和组合对象的访问具有一致性,即以一致的方式处理个别对象以及组合对象,不以层级大小(高低)定义类,都是结点类(抽象类或接口)的子类或实现类每层结点聚合下层结点(以结点的抽象类形式聚合,叶子结点没有下层,没有聚合)

 

  10、外观模式

  外观模式(Facade)也叫过程模式,为子系统中的一组接口,提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用;通过定义一致的接口(界面类),用于屏蔽内部子系统的细节,使得调用端只需要跟这个接口发生调用,而无需关心这个子系统的内部细节(子系统对象在外观类的聚合,使用单例模式)

  11、享元模式

  享元模式(Flyweight),我们可以将对象的状态分为“外部状态”和“内部状态”,将可以被共享(不会变化)的状态作为内部状态存储在对象中,而外部对象(外部状态)、我们可以在适当的时候将外部对象作为参数传递给对象,Flyweight 模式在实现过程中主要是要为共享对象提供一个存放的“仓库”(对象池),仓库的GetFlyweight方法的设计为:遍历仓库,有要查找的对象则返回该对象,否则把查找的对象插入到仓库,享元模式的设计目的是为了减少对象的创建,采取共享对象策略

  12、代理模式

  代理模式(Proxy),为一个对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象,可以在目标 对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能

  三、行为型模式

  13、模板方法模式

  模板方法模式(模板模式),在一个抽象类公开定义了执行它的方法的模板(确定了除了模板方法的其他方法的执行顺序结构 ),它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行(执行模板方法)

  

  模板模式的抽象类的钩子方法,钩子方法什么也不做,抽象类的子类,视需要重写钩子方法,比如抽象类的钩子方法返回值为false,子类重写返回 true,可以决定是否在模板方法里执行钩子限定下的方法

  14、命令模式

  命令模式,为了消除发送者与接收者之间的耦合,在两者之间设定一个命令执行接口(可以发送命令,可以解除命令),发送者聚合接口,接收者聚合接口实现,接收者通过接口与发送者产生联系

  15、访问者模式

  访问者模式(Visitor Pattern ),封装一些作用于某种数据结构的各元素的操作,可以在不改变数据结构的前提下,定义作用于这些元素的新的操作实现了,数据结构和操作的分离,解决了数据结构和操作的耦合性问题,在被访问者的类里面加一个对外提供接待访问者的接口,访问者模式符合单一职责原则,适用于数据结构相对稳定的系统,但是,违背迪米特法则、违背依赖倒转原则

  16、迭代器模式

  迭代器模式提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即不暴露其内部的结构,迭代器接口是系统提供的,含有 hasNext,next,remove

  17、观察者模式

  观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新,被依赖的对象为Subject,依赖的对象为Observer,Subject通知Observer变化;出版者+订阅者=观察者模式,出版者称为主题(Subject),订阅者称为观察者(Observer)

  主题唯一依赖的东西是实现Observer接口的对象列表可以随时增加删除观察者主题或观察者独立改变主题或观察者其中一方,不会影响另一方

  继承Observable可观察类:

  18、中介者模式

  中介者模式(Mediator Pattern)用一个中介对象来封装一系列的对象交互,中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互

  19、备忘录模式

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

 

  20、解释器模式

  解释器模式(Interpreter Pattern),是指给定一个语言(表达式),定义它的文法的一种表示并定义一个解释器,使用该解释器来解释语言中的句子(表达式)

 

  21、状态模式

  状态模式(State Pattern),它主要是解决对象在多种状态转换时,需要对外输出不同的行为的问题,状态和行为是一一对应的,状态之间可以相互转换,当一个对象的内在状态改变时,允许改变其行为,这对象看起来像是改变了其类

  22、策略模式

  策略模式(Strategy Pattern),核心思想是,多用组合/聚合,少用继承,用行为类组合,而不是行为的继承;关闭修改,开放扩展,增加行为不修改代码;每增加一个策略就要增加一个类,策略过多导致类数目庞大;策略模式定义算法族分别封装起来,让它们之间可以替换算法的变化独立于使用算法的客户

  具体实现:定义若干行为接口每个接口由不同的行为的类继承并实现接口客户类聚合行为接口作为客户类属性),产生行为的多态和行为的多样性

  找到应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起,把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分

  针对接口编程,而不是针对实现编程;超类型不需要知道行为的实现细节(委托行为类:超类型包含行为类对象及超类型的行为方法,超类型的行为方法里行为类对象调用行为类的方法),由行为类实现行为接口;声明类时不用理会以后执行时的真正对象类型,变量的声明类型应该是超类型(多态),如此,只要是具体实现此超类型的类所产生的对象,都可以指定给这个变量;超类型的子类可以在子类构造函数中设定行为对象,也可以在超类型设计set方法,设定行为对象,动态改变子类的行为

  23、职责链模式

  职责链模式,也叫责任链模式,为请求创建一个接收者对象的链,目的对请求的发送者和接受者进行解耦;每个接收者都包含对另一个接收者的引用,不能处理会把请求传给下一个接收者;发送者不需要知道由哪个接收者处理,由接收者链处理

  具体实现:创建一个抽象类该类的一个属性包含该类的一个引用,用于指向下一个类对象(设计一个set方法指向下一个职责的类对象 ),同时包含处理发送者请求的抽象函数处理请求,由若干不同职责的类(分别处理不同请求)继承该抽象类并实现处理函数;使用时,创建一个发送者类对象创建若干不同职责的类对象,这些不同职责类对象以职责形成一个环链(调用set方法,传下一个职责类对象);无论把发送者对象传递给任何一个职责对象(职责对象调用处理函数函数的参数是发送者对象),该发送者对象都会被处理