跳至主要內容

软件设计模式

TenSoFlow...大约 31 分钟软件设计模式软件设计模式

软件设计模式

简介

软件工程中,设计模式(Design Pattern)是对软件设计中普遍存在的各种问题,所提出的解决方案。是从建筑设计领域引入计算机科学的。设计模式是为了让软件具有更好的代码重用性可读性可扩展性可靠性

UML图

UML简介

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

UML分类

分为用例图、静态结构图、动态行为图。

  • 用例图

  • 静态结构图:类图、对象图、包图、组件图、部署图

  • 动态行为图:交互图、状态图、活动图

UML类图

用于描述系统中类本身的组成和类之间的各种静态关系。

类之间的关系:依赖泛化实现关联聚合组合

  • 依赖:只要是在类中用到了对方,则就存在依赖关系。使用方指向依赖方。

  • 泛化:泛化实际就是继承,是依赖关系的一种。子类指向父类

  • 实现:实现接口就是实现关系,是依赖关系的一种。由实现方指向接口方。

  • 关联:类与类之间的联系,是依赖关系的一种。具有导航性,即双向关系或单向关系。A类用到了B类则由A指向B是单向关系。如果B类也用到了A类,则B类也要指向A类是双向关系。

  • 聚合:表示整体和部分的关系,整体和部分可以分开,聚合关系是关联关系的特例。具有关联的导航性与多重性。A类使用到了B类,则空心菱形指向A类。

  • 组合:表示整体和部分的关系,整体和部分不可以分开。比如A类使用了B类,A类消失B类也消失则说明其不能分开是组合关系。这种情况一般是A类中有一个B类的属性,并且直接new出来了一个B类对象。实心菱形指向A类。

类图画法

package com.tensoflow;

public class Person {
    private Integer id;
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

七大原则

设计模式原则,是程序员在编程时应当遵守的原则。也是各种设计模式的基础,是设计模式为什么这样设计的依据。

单一职责原则

对类来说一个类应该只负责一项职责。如果A类负责职责1、职责2两个不同的职责当职责1需求变更而改变A时,可能造成职责2执行错误。此时应将A类分成两份分别负责职责1和职责2。

package com.tensoflow.principle.singleResponsibility.problem;

class Vehicle {
    public void run(String vehicle) {
        System.out.println(vehicle + " 在公路上运行...");
    }
}

/*
  Vehicle交通工具类: run()方法违反了单一职责原则
  解决方案: 应该根据交通工具运行方法不同,分解成不同的类
 */
public class SingleResponsibility {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("摩托车");
        vehicle.run("汽车");
        // 飞机不能在公路上运行
        vehicle.run("飞机");
    }
}

通常情况下,我们应当遵守单一职责原则。只有逻辑足够简单时才可以在代码级别违反单一职责原则。只有类中方法数量足够少时才可以在方法级别上保持单一职责原则

迪米特法则

一个对象应该对其它对象保持最少的了解。类与类关系越密切,耦合度越大。迪米特法则又叫最少知道原则。即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外泄露任何信息。

package com.tensoflow.principle.demeter.problem;

import lombok.Data;

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

// 学校总部员工类
@Data
class Employee {
    private String id;
}

// 学院员工类
@Data
class CollegeEmployee {
    private String id;
}

// 管理学院员工的管理类
class CollegeManagement {
    public List<CollegeEmployee> getAllEmployee() {
        List<CollegeEmployee> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            CollegeEmployee emp = new CollegeEmployee();
            emp.setId("学院员工Id = " + i);
            list.add(emp);
        }
        return list;
    }
}

/*
  管理学校总部员工的管理类
  SchoolManagement的直接朋友:Employee、CollegeManagement
  SchoolManagement的间接朋友:CollegeEmployee 这样违背了迪米特法则
 */
class SchoolManagement {
    public List<Employee> getAllEmployee() {
        List<Employee> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Employee emp = new Employee();
            emp.setId("学校总部员工Id = " + i);
            list.add(emp);
        }
        return list;
    }

    // 输出学校总部和学院员工信息的方法
    void printAllEmployee(CollegeManagement sub) {
        // 输出学院员工信息的代码不应该写在学校管理类中,应写到管理学院的类中
        List<CollegeEmployee> list1 = sub.getAllEmployee();
        System.out.println("--------------分公司员工--------------");
        for(CollegeEmployee e : list1) {
            System.out.println(e.getId());
        }

        List<Employee> list2 = this.getAllEmployee();
        System.out.println("--------------学校总部员工--------------");
        for(Employee e : list2) {
            System.out.println(e.getId());
        }
    }
}

public class demeter {
    public static void main(String[] args) {
        SchoolManagement schoolManagement = new SchoolManagement();
        schoolManagement.printAllEmployee(new CollegeManagement());
    }
}

接口隔离原则

客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。

package com.tensoflow.principle.segregation.problem;

interface Interface1 {
    void operation1();
    void operation2();
    void operation3();
    void operation4();
    void operation5();
}

class B implements Interface1 {

    @Override
    public void operation1() {
        System.out.println("B 实现了 operation1");
    }

    @Override
    public void operation2() {
        System.out.println("B 实现了 operation2");
    }

    @Override
    public void operation3() {
        System.out.println("B 实现了 operation3");
    }

    @Override
    public void operation4() {
        System.out.println("B 实现了 operation4");
    }

    @Override
    public void operation5() {
        System.out.println("B 实现了 operation5");
    }
}

class D implements Interface1 {

    @Override
    public void operation1() {
        System.out.println("D 实现了 operation1");
    }

    @Override
    public void operation2() {
        System.out.println("D 实现了 operation2");
    }

    @Override
    public void operation3() {
        System.out.println("D 实现了 operation3");
    }

    @Override
    public void operation4() {
        System.out.println("D 实现了 operation4");
    }

    @Override
    public void operation5() {
        System.out.println("D 实现了 operation5");
    }
}

// A类通过Interface1接口依赖(使用)B类,只会用到1,2,3个方法
class A {
    public void depend1(Interface1 i) {
        i.operation1();
    }
    public void depend2(Interface1 i) {
        i.operation2();
    }
    public void depend3(Interface1 i) {
        i.operation3();
    }
}

// C类通过Interface1接口依赖(使用)D类,只会用到1,4,5个方法
class C {
    public void depend1(Interface1 i) {
        i.operation1();
    }
    public void depend4(Interface1 i) {
        i.operation4();
    }
    public void depend5(Interface1 i) {
        i.operation5();
    }
}

/*
问题分析:
  B类实现了Interface1接口
  A类通过Interface1接口依赖B类
  但是接口对于A类来说不是最小接口,因为A类只会使用接口中的1,2,3方法
  B类需要实现接口的所有方法。造成了B类实现了根本不需要实现的方法
解决方式:
  将接口Interface1拆分为独立的几个接口,A类和C类分别与它们需要的接口建立依赖关系
 */
public class Segregation {
    public static void main(String[] args) {
        A a = new A();
        a.depend1(new B());
        a.depend2(new B());
        a.depend3(new B());
    }
}

依赖倒转原则

  1. 高层模块不应该依赖底层模块,二者都应该依赖其抽象

  2. 抽象不应该依赖细节,细节应该依赖抽象

  3. 依赖倒转原则的中心思想:面向接口编程

  4. 依赖倒转原则的设计理念:相对于细节的多变性,抽象的东西要稳定的多,以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在Java中,抽象指的是接口或抽象类,细节就是具体的实现类。

  5. 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

package com.tensoflow.principle.inversion.problem;

class Email {
    public String getInfo() {
        return "电子邮件信息: Hello World!";
    }
}

class Person {
    public void receive(Email email) {
        System.out.println(email.getInfo());
    }
}

/*
  Person有接收邮件消息的功能
  问题分析:
    如果需要获取的对象是微信、短信等。则需要增加新的类并且Person也要增加相应的接收方法
  解决方案:
    引入一个抽象的接口IReceiver,表示接收者让Person类与接口发生依赖。
    然后让Email、WeiXin等去实现IReceiver接口。
 */
public class Inversion {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());
    }
}

里氏替换原则

在使用继承时,在子类中尽量不要重写父类的方法。在适当的情况下,可以通过聚合、组合、依赖的来解决问题。

package com.tensoflow.principle.liskov.problem;

class A {
    // 两数求差
    public int func1(int a, int b) {
        return a - b;
    }
}

// B类继承了A
// 并增加了一个新功能:完成两个数相加然后和9求和
class B extends A {
    // B类重写了父类方法,改变了其逻辑
    @Override
    public int func1(int a, int b) {
        return a + b;
    }

    // 新功能:完成两个数相加然后和9求和
    public int func2(int a, int b) {
        return func1(a, b) + 9;
    }
}

/*
  问题分析:
    原来运行正常的求差功能发生了错误,原因是B类无语重写了父类方法,造成原有功能出现错误。
    在实际编程中常常会重写父类方法完成新功能,这样写起来虽然简单,但是整个集成体系的复用性会比较差。
    特别是运行多态比较频繁的时候。
  解决方案:
    原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合、组合等关系代替。
 */
public class Liskov {
    public static void main(String[] args) {
        A a = new A();
        System.out.println("11 - 3 = " + a.func1(11, 3));

        System.out.println("-------------------");
        B b = new B();
        // 这里本意是求出11 - 3的结果但是B已经重写了A中的方法造成结果错误
        System.out.println("11 - 3 = " + b.func1(11, 3));
        System.out.println("11 + 3 + 9 = " + b.func2(11, 3));
    }
}

合成复用原则

原则是尽量使用合成或聚合的方式,而不是使用继承。

开闭原则

模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。

package com.tensoflow.principle.openClose.problem;

// 绘图类 (使用方)
class GraphicEditor {
    public void drawShape(Shape s) {
        if (s.m_type == 1) {
            drawRectangle(s);
        } else if (s.m_type == 2) {
            drawCircle(s);
        }
    }

    private void drawRectangle(Shape r) {
        System.out.println("矩形");
    }

    private void drawCircle(Shape r) {
        System.out.println("圆形");
    }
}

class Shape {
    int m_type;
}

class Rectangle extends Shape {
    Rectangle() {
        super.m_type = 1;
    }
}

class Circle extends Shape {
    Circle() {
        super.m_type = 2;
    }
}

/*
  问题分析:
    违反了开闭原则,如果需要增加一个绘制三角形的方法需要更改绘图类(使用方)的代码
  解决方案:
    把Shape类做成抽象类,并提供一个抽象的draw方法,打子类去实现即可
 */
public class OpenClose {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
    }
}

创建型模式

单例模式

采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。JDK中Runtime类就是经典的单例模式。

饿汉式(静态变量) -- 可以使用

package com.tensoflow.designPattern.singleton.way01;

/**
 * 饿汉式(静态变量)
 */
class MySingleton {
    // 构造器私有化,外部不能通过new关键字创建实例
    private MySingleton() {}

    // 本地内部创建对象实例
    private final static MySingleton instance = new MySingleton();

    // 对外提供一个公有的静态方法,返回实例对象
    public static MySingleton getInstance() {
        return instance;
    }
}

public class Singleton {
    public static void main(String[] args) {
        MySingleton instance = MySingleton.getInstance();
        MySingleton instance1 = MySingleton.getInstance();
        System.out.println(instance == instance1); // true
    }
}

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

缺点:在类装载的时候就完成实例化,没有达到懒加载的效果,如果从始至终从未使用过这个实例,则会造成内存浪费

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

饿汉式(静态代码块) -- 不推荐使用

package com.tensoflow.designPattern.singleton.way02;

/**
 * 饿汉式(静态代码块)
 */
class MySingleton {
    // 构造器私有化,外部不能通过new关键字创建实例
    private MySingleton() {}

    // 定义私有属性
    private static MySingleton instance;

    // 静态代码块中创建对象实例
    static {
        instance = new MySingleton();
    }

    // 对外提供一个公有的静态方法,返回实例对象
    public static MySingleton getInstance() {
        return instance;
    }
}

public class Singleton {
    public static void main(String[] args) {
        MySingleton instance = MySingleton.getInstance();
        MySingleton instance1 = MySingleton.getInstance();
        System.out.println(instance == instance1); // true
    }
}

饿汉式(静态代码块)和饿汉式(静态变量)方式一样,只不过创建实例位置在静态代码块中。

懒汉式(线程不安全) -- 不推荐使用

package com.tensoflow.designPattern.singleton.way03;

/**
 * 懒汉式(线程不安全)
 */
class MySingleton {
    // 构造器私有化,外部不能通过new关键字创建实例
    private MySingleton() {}

    // 定义私有属性
    private static MySingleton instance;

    // 对外提供一个公有的静态方法,返回实例对象
    public static MySingleton getInstance() {
        if (null == instance) {
            instance = new MySingleton();
        }
        return instance;
    }
}

public class Singleton {
    public static void main(String[] args) {
        MySingleton instance = MySingleton.getInstance();
        MySingleton instance1 = MySingleton.getInstance();
        System.out.println(instance == instance1); // true
    }
}

起到了懒加载的效果,但是只能在单线程下使用。

懒汉式(线程安全-效率低) -- 不推荐使用

package com.tensoflow.designPattern.singleton.way04;

/**
 * 懒汉式(线程安全-效率低)
 */
class MySingleton {
    // 构造器私有化,外部不能通过new关键字创建实例
    private MySingleton() {}

    // 定义私有属性
    private static MySingleton instance;

    // 对外提供一个公有的静态方法,加入synchronized,返回实例对象
    public static synchronized MySingleton getInstance() {
        if (null == instance) {
            instance = new MySingleton();
        }
        return instance;
    }
}

public class Singleton {
    public static void main(String[] args) {
        MySingleton instance = MySingleton.getInstance();
        MySingleton instance1 = MySingleton.getInstance();
        System.out.println(instance == instance1); // true
    }
}

达到了懒加载的效果,也达到了线程安全,但是效率低

懒汉式(线程安全-效率高) -- 推荐使用

package com.tensoflow.designPattern.singleton.way05;

/**
 * 懒汉式(线程安全-效率高)
 */
class MySingleton {
    // 构造器私有化,外部不能通过new关键字创建实例
    private MySingleton() {}

    // 定义私有属性 volatile可以立即更新到主存
    private static volatile MySingleton instance;

    // 对外提供一个公有的静态方法,返回实例对象
    public static MySingleton getInstance() {
        if (null == instance) {
            synchronized (MySingleton.class) {
                if (null == instance) {
                    instance = new MySingleton();
                }
            }
        }
        return instance;
    }
}

public class Singleton {
    public static void main(String[] args) {
        MySingleton instance = MySingleton.getInstance();
        MySingleton instance1 = MySingleton.getInstance();
        System.out.println(instance == instance1); // true
    }
}

达到了懒加载的效果,也达到了线程安全,并且效率高

静态内部类 -- 推荐使用

package com.tensoflow.designPattern.singleton.way06;

/**
 * 静态内部类
 */
class MySingleton {
    // 构造器私有化,外部不能通过new关键字创建实例
    private MySingleton() {}

    // 静态内部类
    private static class SingletonInstance {
        private static final MySingleton INSTANCE = new MySingleton();
    }

    // 对外提供一个公有的静态方法,返回实例对象
    public static MySingleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

public class Singleton {
    public static void main(String[] args) {
        MySingleton instance = MySingleton.getInstance();
        MySingleton instance1 = MySingleton.getInstance();
        System.out.println(instance == instance1); // true
    }
}

线程安全,实现了延迟加载效率高

枚举 -- 推荐使用

package com.tensoflow.designPattern.singleton.way07;

/**
 * 枚举
 */
enum MySingleton {
    INSTANCE;
    public void ok() {
        System.out.println("ok");
    }
}

public class Singleton {
    public static void main(String[] args) {
        MySingleton instance = MySingleton.INSTANCE;
        MySingleton instance1 = MySingleton.INSTANCE;
        System.out.println(instance == instance1); // true
        instance.ok(); // ok
    }
}

线程安全,还能防止反序列化重新创建新的对象。其它所有方法都能通过反射来破坏单例。

简单工厂模式

JDK中的Calendar类就是采用的简单工厂模式。

具体需求
有一个披萨的项目:要便于披萨种类的扩展,也要便于维护
1. 披萨的种类有很多比如GreekPizz、CheesePizz
2. 披萨的制作有prepare、bake、cut、box
3. 完成披萨店订购功能

传统方法

package com.tensoflow.designPattern.factory.simpleFactory.problem;

// 披萨商店类 相当于客户端
public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza();
    }
}

问题分析:上述代码违反了开闭原则,当我们增加一个披萨种类的时候要修改使用方代码即创建Pizze的OrderPizza类。如果在其它地方也要使用创建Pizza的代码,意味着需要多处修改。

解决方案:把创建Pizza对象封装到一个类中,这样有新的Pizza种类时,只需要修改该类就可。其它有创建Pizza对象的代码就不需要修改了即简单工厂模式。

简单工厂模式

简单工厂模式属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。定义一个创建对象的类,由这个类来封装另一个类实例化对象的行为。在软件开发中,当我们会用到大量的创建某一类或者某批对象时,就会使用工厂模式。

package com.tensoflow.designPattern.factory.simpleFactory.improve1;

// 披萨商店类 相当于客户端
public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza();
    }
}

工厂方法模式

新需求:
客户在点披萨时可以点不同口味的披萨。比如北京的奶酪披萨、北京的胡椒披萨、伦敦的奶酪披萨、伦敦的胡椒披萨。
package com.tensoflow.designPattern.factory.factoryMethod;

import com.tensoflow.designPattern.factory.simpleFactory.improve1.OrderPizza;

// 披萨商店类 相当于客户端
public class PizzaStore {
    public static void main(String[] args) {
        // 创建北京口味的各种披萨
        new BJOrderPizza();
        // 创建伦敦口味的各种披萨
        new LDOrderPizza();
    }
}

抽象工厂模式

定义一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类。抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。从设计层面上看,抽象工厂模式就是对简单工厂模式的改进或者称为进一步的抽象。

package com.tensoflow.designPattern.factory.abstractFactory;

// 披萨商店类 相当于客户端
public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza(new BJFactory());
        new OrderPizza(new LDFactory());
    }
}

原型模式

需求:
现在有一只羊。姓名:tom,年龄:1,颜色:白色。请编写程序创建和tom羊属性完全相同的10只羊。

传统方法

package com.tensoflow.designPattern.prototype.problem;

import lombok.Data;

@Data
public class Sheep {

    private String name;

    private Integer age;

    private String color;

    public Sheep(String name, Integer age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }
}

优点是比较好理解,简单易操作。但是在创建新对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低。

原型模式

原型模式(Prototype)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。Java中Object类是所有类的父类,Object类提供了一个clone()方法。该方法可以将一个Java对象复制一份,但是需要实现clone的java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力。Spring中原型Bean的创建就是原型模式。

应用场景

  • 当你有一个配置类 / 常量类(不可变对象),需要基于它做少量修改生成新对象时,直接克隆后修改比重新new并逐个赋值更高效。
  • 需要记录对象在某个时间点的状态(快照),后续操作不影响历史状态。典型如编辑器的撤销、游戏存档、数据审计
  • 多线程环境下的对象隔离。多线程共享一个对象时,如果某个线程需要修改对象数据,但又不想影响其他线程的读取,可克隆出独立副本供当前线程操作。
package com.tensoflow.designPattern.prototype.improve1;

import lombok.Data;

@Data
public class Sheep implements Cloneable {

    private String name;

    private Integer age;

    private String color;

    public Sheep(String name, Integer age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }

    // 重写clone方法 浅拷贝
    @Override
    protected Sheep clone(){
        try {
            return (Sheep) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException("克隆 Sheep 失败", e);
        }
    }
}

建造者模式

需求:
1. 需要盖房子:打桩、砌墙、封顶
2. 房子有各种各样的比如普通房、高楼、别墅。各种房子的过程虽然一样,但是要求不同。
3. 请编写编程,完成需求。

传统方法

package com.tensoflow.designPattern.builder.problem;

public class Client {
    public static void main(String[] args) {
        CommonHouse commonHouse = new CommonHouse();
        commonHouse.build();
    }
}

优点是比较好理解,简单易操作。但是设计的程序结构过于简单,没有设计缓存层对象,程序的扩展和维护性不好。这种设计把产品和创建产品的过程封装在一起,耦合性增强了。

建造者模式

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

建造者模式的四个角色:

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

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

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

Director(指挥者):构建一个使用Builder接口的对象。主要用于创建一个复杂的对象。能隔离客户与对象的生产过程,还负责控制产品对象的生产过程。

package com.tensoflow.designPattern.builder.improve1;

public class Client {
    public static void main(String[] args) {

        HouseDirector houseDirector = new HouseDirector(new HighBuilder());
        House house = houseDirector.constructHouse();

        houseDirector = new HouseDirector(new CommonBuilder());
        House house1 = houseDirector.constructHouse();

    }
}

适配器模式

适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示。主要目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。分为类适配器模式、对象适配器模式、接口适配器模式。SpringMVC框架中的HandlerAdapter就使用了适配器模式。

类适配器模式

package com.tensoflow.designPattern.adapter.classAdapter;

public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter());
    }
}

类适配器模式

package com.tensoflow.designPattern.adapter.objectAdapter;

public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter(new Voltage220V()));
    }
}

接口适配器模式

当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。适用于一个接口不想使用其所有方法的情况。

package com.tensoflow.designPattern.adapter.interfaceAdapter;

public class Client {
    public static void main(String[] args) {
        AbstractAdapter adapter = new AbstractAdapter() {
            // 只需要去重写我们需要使用的方法
            @Override
            public void method1() {
                System.out.println("我只想使用method1方法");
            }
        };
        adapter.method1();
    }
}

结构型模式

桥接模式

桥接模式(Bridge模式)是指:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。JDBC中的Driver接口就使用了桥接模式。

package com.tensoflow.designPattern.birdge.improve1;

public class Client {
    public static void main(String[] args) {
        Phone xiaoMiPhone = new FoldedPhone(new XiaoMi());
        xiaoMiPhone.open();
        xiaoMiPhone.close();
        xiaoMiPhone.call();

        System.out.println("========================");

        Phone vivoPhone = new UpRightPhone(new Vivo());
        vivoPhone.open();
        vivoPhone.close();
        vivoPhone.call();
    }
}

行为型模式

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8