Java 设计模式

1、设计模式概述

1.1、学习设计模式的重要性
1)软件工程中,设计模式是对软件设计找那个普遍存在(反复出现)的各种问题,所提出的解决方案。这个术语是由挨里希·伽玛(Erich)等人在1990年代从建筑设计领域引入到计算机科学的。

2)是否应用了设计模式的程序的区别就犹如下图中的简易房与大厦。
在这里插入图片描述
3)当我们的一个项目开发完之后,如果客户提出新增功能。这需要我们的程序具有可扩展性,而是否拥有很高的扩展性就依赖于程序的设计。
4)如果项目开发完后,原来的程序员离职了,需要新的人来接手。(维护性【可读性、规范性】)
5)设计模式在软件中哪里。面向对象()->功能模块[设计模式+算法(数据结构)]-> 框架[使用到多种设计模式]->架构[服务器集群]
6)面试需要

1.2、设计模式的目的
编写软件过程中,程序员面临着来自耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性等多方面的挑战,设计模式是为了让程序具有更好的:
1)代码重用性(相同功能的代码,不用多次编写)
2)可读性(即:编程规范性,便于其他程序员的阅读和理解)
3)可扩展性(即:当需要增加新的功能时,非常的方便,称为可维护)
4)可靠性(当我们增加新的功能时,对原来的功能没有影响)
5)使程序呈现高内聚,低耦合的特性
6)设计模式包含了面向对象的精髓,“懂了设计模式,你就懂了面向对象分析和设计(OOA/D)的精要”

2、设计模式七大原则

设计模式原则是程序员在编程时应该准守的原则,也是设计模式设计的依据。

设计模式常用七大原则:
1、单一职责原则 查看
2、接口隔离原则 查看
3、依赖倒置原则查看
4、里氏替换原则查看
5、开闭原则查看
6、迪米特法则查看
7、合成复用原则查看

2.1、单一职责原则
对类来说的,一个类应该只负责一项职责。

例:如果A负责两个不同职责:职责1,职责2:当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A!,A2。

单一职责原则的注意事项与细节:
1)降低类的复杂度,一个类只负责一项职责。
2)提高类的可读性,可维护性。
3)降低变更引起的风险。
4)通常情况下,我们应当遵守单一职责原则,只有逻辑足够建档,才可以在代码级违反单一职责原则,只有类中方法数量足够少,可以在方法级别保持单一职责原则。

2.2、接口隔离原则
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
接口的细粒度越高,可以许多减少不必要的实现。

2.3、依赖倒置原则
高层模块不应该依赖低层模块,二者都应该依赖其抽象。
抽象不应该依赖细节,细节应该依赖抽象。
依赖倒置的中心思想就是面向接口编程。

依赖倒置的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在Java中,抽象指的是接口或抽象类,细节就是具体的实现类。使用接口或抽象类的目的是定制好规范,而不涉及任何具体的操作,将展现细节的任务交给他们的实现类去完成。

依赖倒置原则的注意事项与细节:
1)低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好。
2)变量的声明类型尽量使用抽象类或接口,这样我们的变量引用与实际对象之间,就存在一个缓冲层,利于程序扩展和优化。
3)继承时遵循理氏替换原则。

2.4、理氏替换原则
继承的思考:
1)继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类都必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
2)继承在给程序设计带来便利的同时,也带来了弊端,比如使用继承会给程序带来侵入性,程序的可移植性会降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障。
那么如何正确的使用继承?——遵循理氏替换原则

在使用继承时,遵循理氏替换原则,在子类中尽量不要重写父类的方法。
理氏替换原则告诉我们,继承实际上让两个类耦合增强了,在适当的情况下,可以通过聚合,组合,依赖类解决问题。

2.5、开闭原则
开闭原则的概述
1)开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则。
2)一个软件实体如类,模块和函数应该对扩展开发(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
3)当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
4)编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。

2.6、迪米特法则
迪米特原法则的概述
1)一个对象应该对其他对象保持最少的了解。
2)类与类关系越密切,耦合度越大。
3)迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好,也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外处理提供的public方法,不对外泄露任何信息。
4)迪米特法则还有一个更简单的定义:只与直接的朋友通信。
直接的朋友:
每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。

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

合成复用原则的核心思想
1)找出应用中可能需要变化的地方,将它们独立出来,不要将不需要变化的代码混在一起。
2)针对接口编程,而不是针对实现编程。
3)为了交互对象之间的松耦合设计而努力。

3、UML类图

主要内容:
1、UML基本介绍 查看
2、类之间的关系 查看

3.1、UML基本介绍
UML——Unified modeling language UML(统一建模语言),是一种用于软件系统分析和设计的语言工具,它用于帮助软件开发人员进行思考和记录思路的结果。
UML本身是一套符号的规定,就像数学符合和化学符号一样,这些符号用于描述软件模型中的各个元素和他们之间的关系,比如类、接口、实现、泛化、依赖、组合、聚合等。

UML图分类:
1)用例图
2)静态结构图:类图、对象图、包图、组件图、部署图
3)动态行为图:交互图(时序图与协作图)、状态图、活动图

说明:
1)类图是描述类与类之间的关系的,是UML图中最核心的。

UML类图
用于描述系统中的类(对象)本身的组成和类(对象)之间的各种静态关系。
类之间的关系:依赖、泛化(继承)、实现、关联、聚合与组合。

3.2类之间的关系
依赖关系(Dependence) —— 只要在类中用到了对方,那么他们之间就存在依赖关系。
泛化关系(Generalization) —— 实际上就是继承关系。属于依赖关系的特例。
实现关系(Implementatin)—— 接口与接口实现类之间的关系。依赖关系的特例。
关联关系(Association) —— 类与类之间的联系,具有导航性,即双向关系或单向关系。属于依赖关系的特例。
聚合关系(Aggregation)—— 整体与部分的关系,整合与部分可以分开。属于关联关系的特例。
组合关系(Composition)—— 也是整体与部分的关系,但是整体与部分不可以分开。

4、设计模式的三大类型与概述

设计模式分为三种类型,共23种
1、创建型模式:用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”,它有以下5种。
单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。

2、结构型模式:用于描述如何将类或对象按某种布局组成更大的结构,它有以下7种。
代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
装饰器(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。

3、行为型模式:用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责,它有以下11种。
模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。

5、单例模式(Singleton Pattern)

单例模式采取一定方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
单例模式保证了系统内存中只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是用new。单例模式使用在需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)。

单例模式的基本构造
1)构造器私有化
2)类的内部创建对象
3)向外暴露一个静态的公共方法

单例模式的实现方式:
1)饿汉式(静态常量)详情
2)懒汉式(线程不安全)详情
3)懒汉式(线程安全,同步方法与volatile关键字)详情
4)双重检查 详情
5)静态内部类 详情
6)枚举 详情

5.1、饿汉式单例

public class SingleClass {
    private SingleClass(){ }

    private static SingleClass instance = new SingleClass();

    public static SingleClass getInstance(){
        return instance;
    }
}

这种写法比较简单,在类装载的时候就完成了实例化,避免了线程同步问题,但也因此没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

总结:这种单例模式可用,但有可能会造成内存浪费。

5.2、懒汉式单例

public class Singleton {
    private Singleton(){}
    
    private static Singleton singleton;
    
    public static Singleton getInstance(){
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

懒汉式单例起到了Lazy Loading的效果,但存在线程安全的问题。当一个线程A进入if条件,还没来得及创建Sinleton实例。另一个线程B也进入了if条件,这样就会产生多个实例,也就破坏了单例的结构。

总结:不能在实际开发中使用这种方式。

5.3、懒汉式单例(加同步锁)

public class Singleton {

    private static volatile LazySingleton instance = null;    //保证 instance 在所有线程中同步
    
    private LazySingleton() {
    }    //private 避免类在外部被实例化
    
    public static synchronized LazySingleton getInstance() {
        //getInstance 方法前加同步
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

同步锁的作用就不普及了,讲一下使用Volatile关键字的原因是因为在多线程的情况下,每个线程访问一个变量都会从堆内存中load到当前的线程内存当中保存一个变量副本,以后操作的就是这个变量副本,与堆内存无关,当线程修改后的某一个时刻(线程结束前)就会将修改后的变量写入堆变量中。它的作用是使每个线程的LazySingleton引用指向同一块堆内存。
可以通过以下示例查看给flag变量未添加和添加volatile关键字前后的区别。

 public class VolatileTest extends Thread {

    volatile boolean flag = false;
    int i = 0;

    public void run() {
         while (!flag) {
             i++;
         }
    }

    public static void main(String[] args) throws Exception {
        VolatileTest vt = new VolatileTest();
        vt.start();
         Thread.sleep(2000);
         vt.flag = true;
        System.out.println("stope" + vt.i);
    }
}

总结:以上方式解决了线程不安全的问题,但效率太低。每个线程在想获得类的实例的时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行。方法进行同步效率太低。

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

5.4、双重检查

public class Singleton {
    private Singleton(){ }

    private static Singleton singleton;

    public static  Singleton getInstance(){
        if(singleton == null){
            synchronized (Singleton.class){
                if(singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

Double-Check是多线程开发中常使用的。该方法的特点是:线程安全、延迟加载、避免了进行反复的方法同步,效率更高。

结论:在实际开发中,推荐使用这种单例设计模式。

5.5、静态内部类

public class Singleton {
    private Singleton(){ }

    private static class SingletonInstance{
        private static final Singleton SINGLETON = new Singleton();
    }

    public static  Singleton getInstance(){
        return SingletonInstance.SINGLETON;
    }
}

这种方式采用类装载的机制来来保证初始化实例时只有一个线程。静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。JVM帮助我们保证了线程的安全性,在类的初始化过程中,别的线程是无法进入的。
优点:线程安全、延迟加载、效率高。
结论:推荐使用

5.6、枚举

enum Singleton {
    SINGLETON;

    public void method() {
    }
}

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

6、简单工厂模式(Simpel Factory Pattern)

介绍:简单工厂模式是由一个工厂对象创建出哪一种产品类的实例。它是定义一个创建对象的类,由这个类来封装实例化对象的行为。在软件开发中,当我们需要大量的创建某种、某类、或者某批对象是,就会用到工厂模式。

简单工厂示例:

public interface Product {
    void show();
}

public class ProductA implements Product{
    @Override
    public void show() {
        System.out.println("产品A");
    }
}
public class ProductB implements Product{
    @Override
    public void show() {
        System.out.println("产品B");
    }
}
public class ProductFactory {
    public Product getProduct(String type){
        Product product = null;
        if(type.equals("A")){
            product = new ProductA();
        }else if (type.equals("B")){
            product = new ProductB();
        }
        return product;
    }
}

7、工厂方法模式(Factory Method Pattern)

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

public interface Product {
    void show();
}

public class ProductA implements Product{
    @Override
    public void show() {
        System.out.println("产品A");
    }
}
public class ProductB implements Product{
    @Override
    public void show() {
        System.out.println("产品B");
    }
}
public abstract class ProductFactory {
    public abstract Product getProduct();
}
public class ProductAFactory extends ProductFactory{
    @Override
    public Product getProduct() {
        return new ProductA();
    }
}
public class ProductBFactory extends ProductFactory{
    @Override
    public Product getProduct() {
        return new ProductB();
    }
}

8、抽象工厂模式(Abstract Factory Pattern)

介绍:抽象工厂模式上是定义一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类。它可以将简单工厂模式和工厂方法模式进行整合。从设计层面看,抽象工厂模式就是简单工厂模式的改进(或称进一步的抽象)。将工厂抽象工厂抽象成两层,ABsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

抽象工厂模式示例:

public interface Product {
    void show();
}

public class ProductA implements Product{
    @Override
    public void show() {
        System.out.println("产品A");
    }
}
public class ProductB implements Product{
    @Override
    public void show() {
        System.out.println("产品B");
    }
}

public interface ProductFactory {
    Product getProductA();

    Product getProductB();
}
public class GreeFactory implements ProductFactory{
    @Override
    public Product getProductA() {
        return new ProductA();
    }

    @Override
    public Product getProductB() {
        return new ProductB();
    }
}
public class HaierFactory implements ProductFactory{
    @Override
    public Product getProductA() {
        return new ProductA();
    }

    @Override
    public Product getProductB() {
        return new ProductB();
    }
}

9、原型模式(Prototype Pattern)

原型模式的定义:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象

原型模式的优点:

  1. Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
  2. 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。

原型模式下的主要角色:

  1. 抽象原型类:规定了具体原型对象必须实现的接口。
  2. 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
  3. 访问类:使用具体原型类中的 clone() 方法来复制新的对象。

原型模式的实现——深克隆与浅克隆

  • 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
  • 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

浅克隆实现:使用Object的clone方法
Java 中的 Object 类提供了浅克隆的 clone() 方法,具体原型类只要实现 Cloneable 接口就可实现对象的浅克隆,这里的 Cloneable 接口就是抽象原型类。

public class Student implements Cloneable{
    Teacher teacher;
    String name;

    public Student(Teacher teacher, String name) {
        this.teacher = teacher;
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        System.out.println("克隆学生");
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Student s1 = new Student(new Teacher("丁自纯"), "小明");
        Student s2 = (Student) s1.clone();
        System.out.println(s2.name);
        System.out.println((s1.teacher == s2.teacher));
    }
}
class Teacher{
    String name;

    public Teacher(String name) {
        this.name = name;
    }
}

深克隆实现:使用序列化与反序列化。
序列化(Serialization)就是将对象写到流的过程,写到流中的对象是原有的对象的一个拷贝,而原对象仍存在内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且也可以复制其引用的成员对象,因此通过序列化将对象写入一个流中,再从流中将其读出来,就可以实现深克隆。
需要将要序列化的对象实现Serialization接口。

import java.io.*;

public class Student implements Serializable {
    Teacher teacher;
    String name;

    public Student(Teacher teacher, String name) {
        this.teacher = teacher;
        this.name = name;
    }

    public Object deepClone() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }


    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Student s1 = new Student(new Teacher("丁自纯"), "小明");
        Student s2 = (Student) s1.deepClone();
        System.out.println(s2.name);
        System.out.println((s1.teacher == s2.teacher));
    }
}
class Teacher implements Serializable{
    String name;

    public Teacher(String name) {
        this.name = name;
    }
}

10、建造者模式(Builder Pattern)

介绍:建造者模式又叫生成器模式,是一种对象构建模式。它可以将复杂对象的构建过程抽象出来(抽象类别),是这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。建造者模式是一步一步创建一个复杂的对象,它允许用户只需要通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。

建造者模式示例:

public class Product {
    private String partA;
    private String partB;
    private String partC;

    public void setPartA(String partA) {
        this.partA = partA;
    }

    public void setPartB(String partB) {
        this.partB = partB;
    }

    public void setPartC(String partC) {
        this.partC = partC;
    }

    public void show(){//展示产品内容
        System.out.println("{partA:" + partA
                + ",partB:" + partB
                + ",partC:" + partC + "}"
        );
    }
}
public abstract class Builder {

    protected Product product = new Product();

    public abstract void buildParA();

    public abstract void buildPartB();

    public abstract void buildPartC();

    public Product getProduct(){
        return product;
    }

}
public class ConcreteBuilder extends Builder{
    @Override
    public void buildParA() {
        product.setPartA("头");
    }

    @Override
    public void buildPartB() {
        product.setPartB("身体");
    }

    @Override
    public void buildPartC() {
        product.setPartC("腿");
    }
}
public class Director {
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public Product construct(){
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
        return builder.getProduct();
    }
}

public class Client {
    public static void main(String[] args) {
        Builder builder = new ConcreteBuilder();
        Director director = new Director(builder);
        Product product = director.construct();
        product.show();
    }
}

使用建造者模式增加新的具体建造者无需修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。需要注意使用建造者模式创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间差异很大,则不适合使用建造者模式。

11、适配器模式(Adapter Pattern)

介绍:适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主要目的是兼容性,让原因接口不匹配日不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)。

适配器模式的三种形式类适配器模式、对象适配器模式、接口适配器模式

以下适配器模式的核心在于使用适配器类作为转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
类适配器模式示例

//目标接口
interface Target
{
    public void request();
}
//适配者接口
class Adaptee
{
    public void specificRequest()
    {       
        System.out.println("适配者中的业务代码被调用!");
    }
}
//类适配器类
class ClassAdapter extends Adaptee implements Target
{
    public void request()
    {
        specificRequest();
    }
}
//客户端代码
public class ClassAdapterTest
{
    public static void main(String[] args)
    {
        System.out.println("类适配器模式测试:");
        Target target = new ClassAdapter();
        target.request();
    }
}

对象适配器模式示例

//对象适配器类
class ObjectAdapter implements Target
{
    private Adaptee adaptee;
    public ObjectAdapter(Adaptee adaptee)
    {
        this.adaptee=adaptee;
    }
    public void request()
    {
        adaptee.specificRequest();
    }
}
//客户端代码
public class ObjectAdapterTest
{
    public static void main(String[] args)
    {
        System.out.println("对象适配器模式测试:");
        Adaptee adaptee = new Adaptee();
        Target target = new ObjectAdapter(adaptee);
        target.request();
    }
}

接口适配器模式
核心思想:给不想要重写的方法使用默认的实现。

public interface Interface1 {
    void m1();

    void m2();

    void m3();
}

public abstract class Adaptor implements Interface1{
    @Override
    public void m1() {
        System.out.println("Adaptor m1");
    }

    @Override
    public void m2() {
        System.out.println("Adaptor m2");
    }

    @Override
    public void m3() {
        System.out.println("Adaptor m3");
    }
}

public class Client {
    public static void main(String[] args) {
        Adaptor adaptor = new Adaptor() {
            @Override
            public void m3() {
                System.out.println("设配后的接口");
            }
        };
        adaptor.m1();
        adaptor.m2();
        adaptor.m3();
    }
}

12、桥接模式(Bridge Pattern)

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

以上概念可能有些难以理解,那就看看下面应用桥接模式改良手机与其款式与品牌之间的类结构的案例吧。
先来看看它们之间的关系
在这里插入图片描述
根据以上关系图可建立如下类图。
在这里插入图片描述
上面的类结构存在多个弊端,关键还在是类的数量太多繁杂,且类的扩展性极差。不管是新增一个款式还是品牌,都会在原来的结构上添加更多繁杂的类。我们使用桥接模式改装它:
在这里插入图片描述
使用上面这种桥接模式就可以很好的优化我们的之前的系统,再看一下在此模式下新增一个款式或品牌,这个时候就不用在增加冗余的类了。我们可以看看以下代码的实现:

public abstract class Phone {
    protected Brand brand;

    public Phone(Brand brand) {
        this.brand = brand;
    }

    public abstract void show();
}
public interface Brand {
    String brand();
}
public class FoldedPhone extends Phone{
    public FoldedPhone(Brand brand) {
        super(brand);
    }

    @Override
    public void show() {
        System.out.println("折叠式" + brand.brand());
    }
}
public class UprightPhone extends Phone{
    public UprightPhone(Brand brand) {
        super(brand);
    }

    @Override
    public void show() {
        System.out.println("直立式" + brand.brand());
    }
}
public class Vivo implements Brand{
    @Override
    public String  brand() {
        return "Vivo手机";
    }
}
public class XiaoMi implements Brand{
    @Override
    public String brand() {
        return "小米手机";
    }
}
public class Client {
    public static void main(String[] args) {
        Brand xiaomi = new XiaoMi();
        Brand vivo = new Vivo();

        Phone foledXiaoMi = new FoldedPhone(xiaomi);
        Phone foledVivo = new FoldedPhone(vivo);
        Phone uprightXiaoMi = new UprightPhone(xiaomi);
        Phone uprightVivo = new UprightPhone(vivo);

        foledXiaoMi.show();
        foledVivo.show();
        uprightXiaoMi.show();
        uprightVivo.show();
    }
}

13、装饰者模式(Decorate Pattern)

介绍:装饰者模式就是动态的将新功能附加到对象上。我们可以通过下面代码来学习使用装饰者模式完成一个咖啡套餐的系统,根据模式的特点,我更喜欢将这个模式称之为套娃模式。如何来做,来看:

以下某某咖啡厅的套套餐模式:
咖啡套餐 = 咖啡(主商品)+ 牛奶、面包等(附加商品)
主商品有美式咖啡(Americano)、猫屎咖啡(Civet)、意大利浓咖啡(Espresso)
附加商品有巧克力(Chocolate)、牛奶(Milk)、豆浆(SoybeanMilk)

public abstract class Order {
    String name;
    double price;

    public abstract String content();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}
public abstract class Coffee extends Order {
    @Override
    public String content() {
        return getName();
    }
}
public class Espresso extends Coffee {
    public Espresso() {
        setName("意大利浓咖啡");
        setPrice(9.00);
    }
}
public class Civet extends Coffee {
    public Civet() {
        setName("猫屎咖啡");
        setPrice(18.00);
    }
}
public class Americano extends Coffee {
    public Americano() {
        setName("美式咖啡");
        setPrice(10.00);
    }
}
public abstract class Decorate extends Order {
    Order order;

    public Decorate(Order order) {
        this.order = order;
    }

    @Override
    public String content() {
        return super.getName() + "加" + order.content();
    }
}
public class Chocolate extends Decorate {

    public Chocolate(Order order) {
        super(order);
        setName("巧克力");
        setPrice(3.00);
    }
}
public abstract class Decorate extends Order {
    Order order;

    public Decorate(Order order) {
        this.order = order;
    }

    @Override
    public String content() {
        return super.getName() + "加" + order.content();
    }
}
public class Milk extends Decorate{

    public Milk(Order order) {
        super(order);
        setName("热牛奶");
        setPrice(2.00);
    }
}
public class Chocolate extends Decorate {

    public Chocolate(Order order) {
        super(order);
        setName("巧克力");
        setPrice(3.00);
    }
}
public class SoybeanMilk extends Decorate{
    public SoybeanMilk(Order order) {
        super(order);
        setName("豆浆");
        setPrice(1.50);
    }
}
//开始套娃
public class CoffeeBar {
    public static void main(String[] args) {
        Americano americano = new Americano();
        Decorate d1 = new Chocolate(americano);
        Decorate d2 = new SoybeanMilk(d1);
        System.out.println(d2.content());
    }
}

14、组合模式(Composite Pattern)

介绍:组合模式又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。组合模式可以使用户对单个对象和组合对象的访问具有一致性。

下面是使用组合模式构建的一个学校学院类结构

public abstract class Organization {
    private String name;
    private String des;

    protected void add(Organization organization){

    }

    protected void remove(Organization organization){

    }

    public Organization(String name, String des) {
        this.name = name;
        this.des = des;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDes() {
        return des;
    }

    public void setDes(String des) {
        this.des = des;
    }

    public abstract void info();
}
//学校
public class University extends Organization{
    List<Organization> organizationList = new ArrayList<Organization>();

    public University(String name, String des) {
        super(name, des);
    }

    @Override
    protected void add(Organization organization) {
        organizationList.add(organization);
    }

    @Override
    protected void remove(Organization organization) {
        organizationList.remove(organization);
    }

    @Override
    public void info() {
        System.out.println(">>>>>>" + getName());
        for (Organization organization : organizationList) {
            organization.info();
        }
    }
}
//学院
public class College extends Organization{
    List<Organization> organizationList = new ArrayList<Organization>();

    public College(String name,String des){
        super(name,des);
    }

    @Override
    protected void add(Organization organization) {
        organizationList.add(organization);
    }

    @Override
    protected void remove(Organization organization) {
        organizationList.remove(organization);
    }

    @Override
    public void info() {
        System.out.println(">>>>>" + getName());
        for (Organization organization : organizationList) {
            organization.info();
        }
    }
}
//院系
public class Faculty extends Organization{
    public Faculty(String name, String des) {
        super(name, des);
    }

    @Override
    public void info() {
        System.out.println(getName());
    }
}
public class Client {
    public static void main(String[] args) {
        Organization organization = new University("湖南工业大学","112");

        College A = new College("学院A", "1234");
        College B = new College("学院B", "1234");

        Faculty a1 = new Faculty("机电系", "123");
        Faculty a2 = new Faculty("电子商务系", "123");

        A.add(a1);
        A.add(a2);

        organization.add(A);
        organization.add(B);

        organization.info();
    }
}

15、外观模式(Facade Pattern)

介绍:外观模式也叫过程模式,是为子系统中的一组接口提供一致的界面,此模式定义了一个高层接口,这个接口使得子系统更加容易使用。它屏蔽的了内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节。

下面是一个使用外观模式设计的个人影院系统,它将各种子系统的功能集成到一个提供统一管理接口的类中,是使用者能够更加便捷的调用各个系统的方法。

public class DVDPlayer {
    private DVDPlayer(){
    }

    private static DVDPlayer instance = new DVDPlayer();

    public static DVDPlayer getInstance(){
        return instance;
    }

    public void on(){
        System.out.println("DVD ON");
    }

    public void off(){
        System.out.println("DVD OFF");
    }

    public void play(){
        System.out.println("DVD IS PLAY");
    }

    public void pause(){
        System.out.println("DVD IS PAUSE");
    }
}
public class Popcorn {
    private Popcorn() {
    }

    private static Popcorn instance = new Popcorn();

    public static Popcorn getInstance(){
        return instance;
    }

    public void make(){
        System.out.println("Make Popcorn");
    }

    public void fetch(){
        System.out.println("Fetch Popcorn");
    }
}
public class Screen {
    private Screen(){}

    private static Screen instance = new Screen();

    public static Screen getInstance() {
        return instance;
    }

    public void up(){
        System.out.println("Up Screen");
    }

    public void down(){
        System.out.println("Down Screen");
    }
}

public class PersonalCinema {

    private DVDPlayer dvdPlayer;
    private Popcorn popcorn;
    private Screen screen;

    public PersonalCinema() {
        this.dvdPlayer = DVDPlayer.getInstance();
        this.popcorn = Popcorn.getInstance();
        this.screen = Screen.getInstance();
    }

    public void prepare(){
        System.out.println("看电影前的准备...");
        dvdPlayer.on();
        popcorn.make();
        screen.down();
    }

    public void play(){
        System.out.println("开始看电影...");
        dvdPlayer.play();
        popcorn.fetch();
    }

    public void leave(){
        System.out.println("电影结束");
        dvdPlayer.off();
        screen.up();
    }
}
public class Client {
    public static void main(String[] args) {
        PersonalCinema cinema = new PersonalCinema();
        cinema.prepare();
        cinema.play();
        cinema.leave();
    }
}

16、享元模式(Flyweight Pattern)

介绍:享元模式也叫蝇量模式;运用共享技术有效的支持大量细粒度的对象。它能够解决重复对象的内存浪费问题,同时提高效率。经典的享元模式应用有String常量池、数据库连接池、缓冲池等。

案例:使用享元模式复用元素解决不必要的对象创建

public abstract class WebSite {
    public abstract void use();//抽象方法
}
public class ConcreteWebSite extends WebSite{

    private String type = "";//网站发布的形式(类型)

    public ConcreteWebSite(String type) {
        this.type = type;
    }

    @Override
    public void use() {
        System.out.println("网站的发布形式为:" + type);
    }
}
//网站工厂类,根据需求返回一个网站
public class WebSiteFactory {
    private Map<String, ConcreteWebSite> pool = new HashMap<String, ConcreteWebSite>();

    public WebSiteFactory() {
        pool.put("博客",new ConcreteWebSite("博客"));
        pool.put("微信公共号",new ConcreteWebSite("公众号"));
    }

    public WebSite getWebSiteCategory(String type){
        if(!pool.containsKey(type)){
            pool.put(type,new ConcreteWebSite(type));
        }
        return (WebSite)pool.get(type);
    }

    public int getWebSiteCount(){
        return pool.size();
    }
}
public class Client {
    public static void main(String[] args) {
        WebSiteFactory webSiteFactory = new WebSiteFactory();
        WebSite bloggerWebSite = webSiteFactory.getWebSiteCategory("博客");
        bloggerWebSite.use();
        System.out.println(webSiteFactory.getWebSiteCount());

        WebSite weixiWebSite = webSiteFactory.getWebSiteCategory("微信公众号");
        weixiWebSite.use();
        System.out.println(webSiteFactory.getWebSiteCount());
    }
}

17、代理模式(Proxy Pattern)

介绍:代理模式为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问被目标对象。这样做的好处是可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
被代理的对象可以使远程对象、创建开销大的对象或需要安全控制的对象。
代理模式主要有三种形式:静态代理、动态代理(JDK代理又称接口代理)和Cglib代理(可以在内存动态的创建对象,而不需要实现接口,他是属于动态代理的范畴。)

代理模式的实现:
1、静态代理 点击查看
2、JDK API实现动态代理 点击查看
3、Cglib实现动态代理 点击查看

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

应用实例
(1)定义一个接口Teach
(2)目标对象TeachImpl实现接口Teach
(3)定义用于控制访问目标对象的代理对象,该对象同样也要实现Teach接口。
(4)通过代理对象来访问目标对象的方法。

//Teach接口
public interface Teach {
    void each();
}
//目标对象(被代理对象),实现Teach接口
public class TeachImpl implements Teach{
    @Override
    public void each() {
        System.out.println("Java编程思想教学~~~");
    }
}
//代理对象
public class TeachImplProxy implements Teach{

    private Teach teach;

    public TeachImplProxy(Teach teach) {
        this.teach = teach;
    }

    @Override
    public void each() {
        System.out.println("通知学生上课");
        teach.each();
        System.out.println("辅导学生做作业");
    }
}
//客户类
public class Client {
    public static void main(String[] args) {
        Teach teacher = new TeachImpl();//老师
        Teach assistant = new TeachImplProxy(teacher);//助教
        assistant.each();
    }
}

我们可以将以上代理模式中的实例看做是多个角色:
老师(目标对象)负责Teach接口的实现,教授知识。
助教(代理对象)负责调用老师的teach方法,并协助老师与同学的沟通辅导。
学生(客户对象)享受代理对象的服务。

总结:静态代理优缺点
(1)优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展。
(2)缺点:因为代理对象需要与目标对象实现一样的接口,所有会有很多代理类。一旦接口增加方法,目标对象与代理对象都要维护。

使用JDK提供的API实现动态代理
动态代理的基本介绍:
1)代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
2)代理对象是动态在内存中的构建的。
3)动态代理的代理对象是动态生成。

JDK提供的API实现动态代理主要实现:
1.通过Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
nvocationHandler h);生成代理类
2.定义一个实现InvocationHandler接口的事件处理器

public interface Teach {
    void teach();

    void sayHello(String msg);
}
package com.example.jdkdynamicproxy;

public class TeachImpl implements Teach{
    @Override
    public void teach() {
        System.out.println("教授Oracle数据库~~~");
    }

    @Override
    public void sayHello(String msg) {
        System.out.println(msg);
    }
}
package com.example.jdkdynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {

    //被代理的对象(目标对象)
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //给目标对象生成一个代理对象
    public Object getProxyInstance(){
        /*
        public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          nvocationHandler h)
       //1.ClassLoader loader:指定当前目标对象使用的类加载器
       //2.Class<?>[] interfaces:目标对象实现的接口类型,使用泛型方法确认类型
       //3.InvocationHandler h:事件处理器,执行目标对象的方法时,会触发事件处理器方法,会把当前执行的目标对象方法作为参数传入
        */
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    //代理对象对目标对象的代理实现
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("JDK代理开始");
                        //反射机制调用目标对象的方法
                        Object result = method.invoke(target, args);
                        return result;
                    }
                }
        );
    }
}
public class Client {
    public static void main(String[] args) {
        Teach teacher = new TeachImpl();
        Teach proxy = (Teach) new ProxyFactory(teacher).getProxyInstance();//proxy实际上是一个代理对象
        proxy.sayHello("你好,世界!");
    }
}

使用Cglib实现动态代理
使用Cglib使用目标对象子类来时代理,不需要依赖目标对象实现接口。因此它也称为子类代理,它的低层是通过使用字节码处理框架ASM来转换ASM来转换字节码并生成新的类。

使用cglib需要导入依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

使用时需要注意目标对象的方法如果为static/final,那么不会被拦截,即不会执行目标对象额外的页面方法。

 //目标类 不需要实现接口
public class TeachImpl{
    public void teach() {
        System.out.println("教授Oracle数据库~~~");
    }

    public String sayHello(String msg) {
        System.out.println(msg);
        return msg;
    }
}
public class ProxyFactory implements MethodInterceptor {

    //维护一个目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //返回一个代理对象:是target对象的代理对象
    public Object getProxyInstance(){
        //1.创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2.设计父类
        enhancer.setSuperclass(target.getClass());
        //3.设计回调函数
        enhancer.setCallback(this);
        //4.创建子类对象,即代理对象
        return enhancer.create();
    }

    //调用目标对象的方法
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("Cglib代理模式~~开始");
        Object result = method.invoke(target, args);
        return result;
    }
}
public class Client {
    public static void main(String[] args) {
        //创建目标对象
        TeachImpl target = new TeachImpl();
        //获取代理对象,将目标对象交给代理对象
        TeachImpl proxy = (TeachImpl) new ProxyFactory(target).getProxyInstance();
        //执行代理对象的方法,触发intercept方法,从而实现对目标对象的调用
        String result = proxy.sayHello("Hello World!!!");
        System.out.println(result);
    }
}

18、模版方法模式(Template Method Pattern)

介绍:模板方法模式在一个抽象类中公开定义它的方法模板,它的子类可以按需要重写方法实现,但调用讲义抽象类中定义的方式进行。简单的说,模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。

案例:使用模板方法模式设计豆浆制作模板类

public abstract class SoyaMilk {

    //模板方法,make,模板方法可以做成final,不让子类去覆盖
    final void make() {
        select();
        soak();
        if(isAddCondiments()){
            addCondiments();
        }
        beat();
    }

    //选材料
    void select() {
        System.out.println("选择好的新鲜黄豆");
    }

    //浸泡
    void soak(){
        System.out.println("黄豆和配料开始浸泡,需要3个小时");
    }

    //添加配料
    abstract void addCondiments();

    void beat(){
        System.out.println("黄豆和配料放到豆浆机中去打碎");
    }

    //钩子函数,改变算法的结构。这里是是否加入配料。
    boolean isAddCondiments(){
        return true;
    }
}
public class RedReanSoyaMilk extends SoyaMilk {
    @Override
    void addCondiments() {
        System.out.println("加入上好的红豆");
    }
}
public class PeanutSoyaMilk extends SoyaMilk {
    @Override
    void addCondiments() {
        System.out.println("加入上好的花生");
    }
}
public class GeneralSoyaMilk extends SoyaMilk {

    @Override
    boolean isAddCondiments() {
        return false;//去除算法中加入配料的环节
    }

    @Override
    void addCondiments() {
        //空实现
    }
}
public class Client {
    public static void main(String[] args) {
        System.out.println("=========制作花生豆浆========");
        SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
        peanutSoyaMilk.make();

        System.out.println("=========制作红豆豆浆========");
        SoyaMilk redReanSoyaMilk = new RedReanSoyaMilk();
        redReanSoyaMilk.make();

        System.out.println("=========制作普通豆浆========");
        SoyaMilk generalSoyaMilk = new GeneralSoyaMilk();
        generalSoyaMilk.make();
    }
}

19、命令模式(Command Pattern)

介绍:命令模式是将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

案例:使用命令模式控制电灯的开关

public interface Command {
    //执行
    void execute();
}
public class LightOffCommand implements Command{
    LightReceiver light;

    public LightOffCommand(LightReceiver light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.off();
    }
}
public class LightOnCommand implements Command{

    LightReceiver light;

    public LightOnCommand(LightReceiver light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.on();
    }
}
/**
 * 没有任何命令,即空执行:用于初始化每个按钮,对象什么都不做
 * 其实,这也是一种设计模式,可以省掉空判断
 */
public class NoCommand implements Command{
    @Override
    public void execute() {

    }
}
public class LightReceiver {

    public void on(){
        System.out.println("电灯开了...");
    }

    public void off(){
        System.out.println("电灯关闭了...");
    }
}
public class RemoteController {
    //开 按钮的命令组
    Command onCommand;
    Command offCommand;
    Command otherCommand;

    //构造器,完成对按钮的初始化
    public RemoteController(){
        LightReceiver lightReceiver = new LightReceiver();
        onCommand = new LightOnCommand(lightReceiver);
        offCommand = new LightOffCommand(lightReceiver);
        otherCommand = new NoCommand();
    }


    //按下开按钮
    public void onButtonWasPushed(){
        //找到你按下的开的按钮,并调用对应方法
        onCommand.execute();
    }

    //按下关的按钮
    public void offButtonWasPushed(){
        offCommand.execute();
    }

    //其它命令
    public void otherButtonWasPushed(){
        otherCommand.execute();
    }
}
public class Client {
    public static void main(String[] args) {
        RemoteController remoteController = new RemoteController();
        remoteController.onButtonWasPushed();
        remoteController.offButtonWasPushed();
    }
}

20、访问者模式(Visitor Pattern)

介绍:访问者模式封装一些用于各种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用域这些元素的新的操作。主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题。
访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口。
访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作“污染”这些对象的类,,可以选用访问者模式解决。

案例:

public class VisitorPattern {
    public static void main(String[] args) {
        ObjectStructure os = new ObjectStructure();
        os.add(new ConcreteElementA());
        os.add(new ConcreteElementB());
        Visitor visitor = new ConcreteVisitorA();
        os.accept(visitor);
        System.out.println("------------------------");
        visitor = new ConcreteVisitorB();
        os.accept(visitor);
    }
}
//抽象访问者
interface Visitor {
    void visit(ConcreteElementA element);
    void visit(ConcreteElementB element);
}
//具体访问者A类
class ConcreteVisitorA implements Visitor {
    public void visit(ConcreteElementA element) {
        System.out.println("具体访问者A访问-->" + element.operationA());
    }
    public void visit(ConcreteElementB element) {
        System.out.println("具体访问者A访问-->" + element.operationB());
    }
}
//具体访问者B类
class ConcreteVisitorB implements Visitor {
    public void visit(ConcreteElementA element) {
        System.out.println("具体访问者B访问-->" + element.operationA());
    }
    public void visit(ConcreteElementB element) {
        System.out.println("具体访问者B访问-->" + element.operationB());
    }
}
//抽象元素类
interface Element {
    void accept(Visitor visitor);
}
//具体元素A类
class ConcreteElementA implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public String operationA() {
        return "具体元素A的操作。";
    }
}
//具体元素B类
class ConcreteElementB implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public String operationB() {
        return "具体元素B的操作。";
    }
}
//对象结构角色
class ObjectStructure {
    private List<Element> list = new ArrayList<Element>();
    public void accept(Visitor visitor) {
        Iterator<Element> i = list.iterator();
        while (i.hasNext()) {
            ((Element) i.next()).accept(visitor);
        }
    }
    public void add(Element element) {
        list.add(element);
    }
    public void remove(Element element) {
        list.remove(element);
    }
}

21、迭代器模式(Iterator Pattern)

介绍:迭代器模式提供了一种遍历集合元素的统一接口,用一致的方法遍历集合元素,可以使其内部结构不被暴露。

案例:遍历学院及学院下的各院系

public interface College {
    String getName();

    //增加系的方法
    void addDepartment(String name,String desc);

    //返回一个迭代器,遍历
    Iterator createIterator();
}
public class ComputerCollege implements College {

    Department[] departments;
    int numOfDepartment = 0;

    public ComputerCollege() {
        departments = new Department[5];
        addDepartment("Java","Java专业");
        addDepartment("PHP","PHP专业");
        addDepartment("大数据","大数据专业");
    }

    @Override
    public String getName() {
        return "计算机学院";
    }

    @Override
    public void addDepartment(String name, String desc) {
        Department department = new Department(name,desc);
        departments[numOfDepartment] = department;
        numOfDepartment+=1;
    }

    @Override
    public Iterator createIterator() {
        return new ComputerCollegeIterator(departments);
   }
}
public class ComputerCollegeIterator implements Iterator {

    //这里我们需要Department是以怎样的方式存放=>数组
    Department[] departments;
    int position = 0;//遍历的位置

    public ComputerCollegeIterator(Department[] departments) {
        this.departments = departments;
    }

    @Override
    public boolean hasNext() {
        if(position >= departments.length || departments[position] == null){
            return false;
        }
        return true;
    }

    @Override
    public Object next() {
        Department department = departments[position];
        position +=1;
        return department;
    }

    @Override
    public void remove() {

    }

    @Override
    public void forEachRemaining(Consumer action) {

    }
}
public class InfoCollege implements College{

    private List<Department> departmentList;

    public InfoCollege() {
        departmentList = new ArrayList<>();
        addDepartment("信息安全专业","信息安全专业");
        addDepartment("网络安全专业","网络安全专业");
        addDepartment("服务器安全专业","服务器安全专业");
    }

    @Override
    public String getName() {
        return "信息工程学院";
    }

    @Override
    public void addDepartment(String name, String desc) {
        Department department = new Department(name, desc);
        departmentList.add(department);
    }

    @Override
    public Iterator createIterator() {
        return new InfoCollegeIterator(departmentList);
    }
}
public class InfoCollegeIterator implements Iterator {

    private List<Department> departmentList;
    int index = -1;//索引

    public InfoCollegeIterator(List<Department> departmentList) {
        this.departmentList = departmentList;
    }

    @Override
    public boolean hasNext() {
        if(index >= departmentList.size() -1){
            return false;
        }
        index+=1;
        return true;
    }

    @Override
    public Object next() {
        return departmentList.get(index);
    }

    @Override
    public void remove() {

    }

    @Override
    public void forEachRemaining(Consumer action) {

    }
}

//系
public class Department {
    private String name;
    private String desc;

    public Department(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}
public class OutPutImpl {
    //学院集合
    private List<College> collegeList;

    public OutPutImpl(List<College> collegeList) {
        this.collegeList = collegeList;
    }

    //输出学院 输出系
    public void printDepartment(Iterator iterator){
        while(iterator.hasNext()){
            Department d = (Department) iterator.next();
            System.out.println(d.getName());
        }
    }

    public void printCollege(){
        Iterator<College> iterator = collegeList.iterator();
        while(iterator.hasNext()){
            College college = iterator.next();
            System.out.println("===" + college.getName() + "===");
            printDepartment(college.createIterator());
        }
    }
}
public class Client {
    public static void main(String[] args) {
        List<College> collegeList = new ArrayList<College>();
        ComputerCollege computerCollege = new ComputerCollege();
        InfoCollege infoCollege = new InfoCollege();

        collegeList.add(computerCollege);
        collegeList.add(infoCollege);

        OutPutImpl outPut = new OutPutImpl(collegeList);
        outPut.printCollege();
    }
}

22、观察者模式(Observer Pattern)

介绍:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

案例:将天气情况通知订阅的观察者央视新闻与百度

public interface Subject {
	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	public void notifyObservers();
}
public class WeatherData implements Subject {
	private float temperatrue;
	private float pressure;
	private float humidity;
	private List<Observer> observers;

	public WeatherData() {
		observers = new ArrayList<Observer>();
	}

	public float getTemperature() {
		return temperatrue;
	}

	public float getPressure() {
		return pressure;
	}

	public float getHumidity() {
		return humidity;
	}

	public void dataChange() {
		notifyObservers();
	}

	public void setData(float temperature, float pressure, float humidity) {
		this.temperatrue = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		dataChange();
	}

	@Override
	public void registerObserver(Observer o) {
		// TODO Auto-generated method stub
		observers.add(o);
	}

	@Override
	public void removeObserver(Observer o) {
		// TODO Auto-generated method stub
		if(observers.contains(o)) {
			observers.remove(o);
		}
	}

	@Override
	public void notifyObservers() {
		// TODO Auto-generated method stub
		for(int i = 0; i < observers.size(); i++) {
			observers.get(i).update(this.temperatrue, this.pressure, this.humidity);
		}
	}
}

public interface Observer {
	public void update(float temperature, float pressure, float humidity);
}
public class BaiduSite implements Observer {
	private float temperature;
	private float pressure;
	private float humidity;

	public void update(float temperature, float pressure, float humidity) {
		this.temperature = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		display();
	}

	public void display() {
		System.out.println("======百度天气为你播报======");
		System.out.println("今日温度:" + temperature + "℃");
		System.out.println("气压:" + pressure);
		System.out.println("空气湿度:" + humidity);
	}

}
public class NewsBroadcast implements Observer {

	private float temperature;
	private float pressure;
	private float humidity;

	public void update(float temperature, float pressure, float humidity) {
		this.temperature = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		display();
	}

	public void display() {
		System.out.println("======新闻联播为你播报======");
		System.out.println("今日温度:" + temperature + "℃");
		System.out.println("气压:" + pressure);
		System.out.println("空气湿度:" + humidity);
	}
}
public class Client {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();

        //观察者
        NewsBroadcast newsBroadcast = new NewsBroadcast();
        BaiduSite baiduSite = new BaiduSite();

        //添加观察者
        weatherData.registerObserver(newsBroadcast);
        weatherData.registerObserver(baiduSite);
        weatherData.setData(10f, 100f, 30.3f);

        //移除观察者
        weatherData.removeObserver(newsBroadcast);
    }
}

23、中介者模式(Agent Pattern)

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

案例:使用中介者模式构建同事的交流平台

package com.example;

import java.util.ArrayList;
import java.util.List;

public class MediatorPattern {
    public static void main(String[] args) {
        Mediator md = new ConcreteMediator();
        Colleague c1, c2;
        c1 = new ConcreteColleague1();
        c2 = new ConcreteColleague2();
        md.register(c1);
        md.register(c2);
        c1.send();
        System.out.println("-------------");
        c2.send();
    }
}
//抽象中介者
abstract class Mediator {
    public abstract void register(Colleague colleague);
    public abstract void relay(Colleague cl); //转发
}
//具体中介者
class ConcreteMediator extends Mediator {
    private List<Colleague> colleagues = new ArrayList<Colleague>();
    public void register(Colleague colleague) {
        if (!colleagues.contains(colleague)) {
            colleagues.add(colleague);
            colleague.setMedium(this);
        }
    }
    public void relay(Colleague cl) {
        for (Colleague ob : colleagues) {
            if (!ob.equals(cl)) {
                ((Colleague) ob).receive();
            }
        }
    }
}
//抽象同事类
abstract class Colleague {
    protected Mediator mediator;
    public void setMedium(Mediator mediator) {
        this.mediator = mediator;
    }
    public abstract void receive();
    public abstract void send();
}
//具体同事类
class ConcreteColleague1 extends Colleague {
    public void receive() {
        System.out.println("具体同事类1收到请求。");
    }
    public void send() {
        System.out.println("具体同事类1发出请求。");
        mediator.relay(this); //请中介者转发
    }
}
//具体同事类
class ConcreteColleague2 extends Colleague {
    public void receive() {
        System.out.println("具体同事类2收到请求。");
    }
    public void send() {
        System.out.println("具体同事类2发出请求。");
        mediator.relay(this); //请中介者转发
    }
}

24、备忘录模式(Memento Pattern)

介绍:备忘录模式是在不破坏封装性的前提下,捕获一个对象的内部状态,可供与以后将对象恢复到原先保存的状态。

案例

public class MementoPattern {
    public static void main(String[] args) {
        Originator or = new Originator();
        Caretaker cr = new Caretaker();
        or.setState("S0");
        System.out.println("初始状态:" + or.getState());
        cr.setMemento(or.createMemento()); //保存状态
        or.setState("S1");
        System.out.println("新的状态:" + or.getState());
        or.restoreMemento(cr.getMemento()); //恢复状态
        System.out.println("恢复状态:" + or.getState());
    }
}
//备忘录
class Memento {
    private String state;
    public Memento(String state) {
        this.state = state;
    }
    public void setState(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
}
//发起人
class Originator {
    private String state;
    public void setState(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
    public Memento createMemento() {
        return new Memento(state);
    }
    public void restoreMemento(Memento m) {
        this.setState(m.getState());
    }
}
//管理者
class Caretaker {
    private Memento memento;
    public void setMemento(Memento m) {
        memento = m;
    }
    public Memento getMemento() {
        return memento;
    }
}

25、解释器模式(Interpreter Pattern)

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

案例:“韶粵通”公交车卡的读卡器程序

/*文法规则
  <expression> ::= <city>的<person>
  <city> ::= 韶关|广州
  <person> ::= 老人|妇女|儿童
*/
public class InterpreterPatternDemo {
    public static void main(String[] args) {
        Context bus = new Context();
        bus.freeRide("韶关的老人");
        bus.freeRide("韶关的年轻人");
        bus.freeRide("广州的妇女");
        bus.freeRide("广州的儿童");
        bus.freeRide("山东的儿童");
    }
}
//抽象表达式类
interface Expression {
    public boolean interpret(String info);
}
//终结符表达式类
class TerminalExpression implements Expression {
    private Set<String> set = new HashSet<String>();
    public TerminalExpression(String[] data) {
        for (int i = 0; i < data.length; i++) set.add(data[i]);
    }
    public boolean interpret(String info) {
        if (set.contains(info)) {
            return true;
        }
        return false;
    }
}
//非终结符表达式类
class AndExpression implements Expression {
    private Expression city = null;
    private Expression person = null;
    public AndExpression(Expression city, Expression person) {
        this.city = city;
        this.person = person;
    }
    public boolean interpret(String info) {
        String s[] = info.split("的");
        return city.interpret(s[0]) && person.interpret(s[1]);
    }
}
//环境类
class Context {
    private String[] citys = {"韶关", "广州"};
    private String[] persons = {"老人", "妇女", "儿童"};
    private Expression cityPerson;
    public Context() {
        Expression city = new TerminalExpression(citys);
        Expression person = new TerminalExpression(persons);
        cityPerson = new AndExpression(city, person);
    }
    public void freeRide(String info) {
        boolean ok = cityPerson.interpret(info);
        if (ok) System.out.println("您是" + info + ",您本次乘车免费!");
        else System.out.println(info + ",您不是免费人员,本次乘车扣费2元!");
    }
}

26、状态模式(State Pattern)

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

案例:学生成绩的状态转换程序

public class ScoreStateTest {
    public static void main(String[] args) {
        ScoreContext account = new ScoreContext();
        System.out.println("学生成绩状态测试:");
        account.add(30);
        account.add(40);
        account.add(25);
        account.add(-15);
        account.add(-25);
    }
}
//环境类
class ScoreContext {
    private AbstractState state;
    ScoreContext() {
        state = new LowState(this);
    }
    public void setState(AbstractState state) {
        this.state = state;
    }
    public AbstractState getState() {
        return state;
    }
    public void add(int score) {
        state.addScore(score);
    }
}
//抽象状态类
abstract class AbstractState {
    protected ScoreContext hj;  //环境
    protected String stateName; //状态名
    protected int score; //分数
    public abstract void checkState(); //检查当前状态
    public void addScore(int x) {
        score += x;
        System.out.print("加上:" + x + "分,\t当前分数:" + score);
        checkState();
        System.out.println("分,\t当前状态:" + hj.getState().stateName);
    }
}
//具体状态类:不及格
class LowState extends AbstractState {
    public LowState(ScoreContext h) {
        hj = h;
        stateName = "不及格";
        score = 0;
    }
    public LowState(AbstractState state) {
        hj = state.hj;
        stateName = "不及格";
        score = state.score;
    }
    public void checkState() {
        if (score >= 90) {
            hj.setState(new HighState(this));
        } else if (score >= 60) {
            hj.setState(new MiddleState(this));
        }
    }
}
//具体状态类:中等
class MiddleState extends AbstractState {
    public MiddleState(AbstractState state) {
        hj = state.hj;
        stateName = "中等";
        score = state.score;
    }
    public void checkState() {
        if (score < 60) {
            hj.setState(new LowState(this));
        } else if (score >= 90) {
            hj.setState(new HighState(this));
        }
    }
}
//具体状态类:优秀
class HighState extends AbstractState {
    public HighState(AbstractState state) {
        hj = state.hj;
        stateName = "优秀";
        score = state.score;
    }
    public void checkState() {
        if (score < 60) {
            hj.setState(new LowState(this));
        } else if (score < 90) {
            hj.setState(new MiddleState(this));
        }
    }
}

27、策略模式(Strategy Pattern)

介绍:策略模式是定义一个算法簇,分别封装起来,让它们之间可以相互替换。且算法的变化独立于使用算法的客户。

案例

public class StrategyPattern {
    public static void main(String[] args) {
        Context c = new Context();
        Strategy s = new ConcreteStrategyA();
        c.setStrategy(s);
        c.strategyMethod();
        System.out.println("-----------------");
        s = new ConcreteStrategyB();
        c.setStrategy(s);
        c.strategyMethod();
    }
}
//抽象策略类
interface Strategy {
    public void strategyMethod();    //策略方法
}
//具体策略类A
class ConcreteStrategyA implements Strategy {
    public void strategyMethod() {
        System.out.println("具体策略A的策略方法被访问!");
    }
}
//具体策略类B
class ConcreteStrategyB implements Strategy {
    public void strategyMethod() {
        System.out.println("具体策略B的策略方法被访问!");
    }
}
//环境类
class Context {
    private Strategy strategy;
    public Strategy getStrategy() {
        return strategy;
    }
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    public void strategyMethod() {
        strategy.strategyMethod();
    }
}

28、职责链模式(Chain of Reponsibility Pattern)

介绍:职责链模式又叫责任链模式,职责链模式为请求创建了一个接收者对象的链。链中的每个接受者都包含另一个接收者的引用。如果当前对象不能处理该请求,那么它就会将请求传递给下一个接受者,依此类推。

案例:请假条审批

public class LeaveApprovalTest {
    public static void main(String[] args) {
        //组装责任链
        Leader teacher1 = new ClassAdviser();
        Leader teacher2 = new DepartmentHead();
        Leader teacher3 = new Dean();
        //Leader teacher4=new DeanOfStudies();
        teacher1.setNext(teacher2);
        teacher2.setNext(teacher3);
        //teacher3.setNext(teacher4);
        //提交请求
        teacher1.handleRequest(8);
    }
}
//抽象处理者:领导类
abstract class Leader {
    private Leader next;
    public void setNext(Leader next) {
        this.next = next;
    }
    public Leader getNext() {
        return next;
    }
    //处理请求的方法
    public abstract void handleRequest(int LeaveDays);
}
//具体处理者1:班主任类
class ClassAdviser extends Leader {
    public void handleRequest(int LeaveDays) {
        if (LeaveDays <= 2) {
            System.out.println("班主任批准您请假" + LeaveDays + "天。");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(LeaveDays);
            } else {
                System.out.println("请假天数太多,没有人批准该假条!");
            }
        }
    }
}
//具体处理者2:系主任类
class DepartmentHead extends Leader {
    public void handleRequest(int LeaveDays) {
        if (LeaveDays <= 7) {
            System.out.println("系主任批准您请假" + LeaveDays + "天。");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(LeaveDays);
            } else {
                System.out.println("请假天数太多,没有人批准该假条!");
            }
        }
    }
}
//具体处理者3:院长类
class Dean extends Leader {
    public void handleRequest(int LeaveDays) {
        if (LeaveDays <= 10) {
            System.out.println("院长批准您请假" + LeaveDays + "天。");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(LeaveDays);
            } else {
                System.out.println("请假天数太多,没有人批准该假条!");
            }
        }
    }
}
//具体处理者4:教务处长类
class DeanOfStudies extends Leader {
    public void handleRequest(int LeaveDays) {
        if (LeaveDays <= 20) {
            System.out.println("教务处长批准您请假" + LeaveDays + "天。");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(LeaveDays);
            } else {
                System.out.println("请假天数太多,没有人批准该假条!");
            }
        }
    }
}

学习心得

设计模式不是为了使用而使用,应该是为了解决某些问题。如为了增加程序的扩展性,可维护性(可读性)或为了是提高系统的可靠性,优化系统的性能。在实际开发中,也可以多种设计模式结合使用。最重要的是理解它的核心思想,什么地方用到何种设计模式有怎么样的好处。开发时一定不能拘泥于形,设计应该要可以灵活多变。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值