跳至主要內容

软件设计模式

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

软件设计模式

简介

软件工程中,设计模式(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;

public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("tom", 1, "白色");

        Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());

        System.out.println(sheep);
        System.out.println(sheep1);
        System.out.println(sheep2);
        System.out.println(sheep3);
        System.out.println(sheep4);
        System.out.println(sheep5);
    }
}

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

原型模式

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

应用场景

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

public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("tom", 1, "白色");

        Sheep sheep1 = sheep.clone();
        Sheep sheep2 = sheep.clone();
        Sheep sheep3 = sheep.clone();
        Sheep sheep4 = sheep.clone();
        Sheep sheep5 = sheep.clone();

        System.out.println(sheep);
        System.out.println(sheep1);
        System.out.println(sheep2);
        System.out.println(sheep3);
        System.out.println(sheep4);
        System.out.println(sheep5);

    }
}

建造者模式

需求:
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();
    }
}

装饰者模式

装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,符合开闭原则。Java的IO流中的FilterInputStream就是装饰者模式。

星巴克咖啡订单项目需求
1. 咖啡种类:Espresso、ShortBlack、LongBlack、Decaf
2. 调料:Milk、Soy(豆浆)、Chocolate
3. 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
4. 使用面向对象来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡 + 调料组合
package com.tensoflow.designPattern.structure.decorator.improve1;

public class Client {
    public static void main(String[] args) {
        // 单点LongBlack咖啡
        Drink coffee = new LongBlack();
        System.out.println(coffee.cost());
        System.out.println(coffee.getDescription());

        // 向LongBlack咖啡中增加Chocolate
        coffee = new Chocolate(coffee);
        System.out.println(coffee.cost());
        System.out.println(coffee.getDescription());

        // 向LongBlack咖啡中增加Milk
        coffee = new Milk(coffee);
        System.out.println(coffee.cost());
        System.out.println(coffee.getDescription());
    }
}

组合模式

组合模式:又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示整体-部分的层次关系。Java中的HashMap就使用到了组合模式。

需求:
编写程序展示一个学校院系结构:要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。
package com.tensoflow.designPattern.structure.composite.improve1;

public class Client {
    public static void main(String[] args) {
        OrganizationComponent university = new University("清华大学", "中国顶级理科大学");

        OrganizationComponent computerCollege = new College("计算机学院", "计算机学院");
        OrganizationComponent infoEngineerCollege = new College("信息工程学院", "信息工程学院");

        university.add(computerCollege);
        university.add(infoEngineerCollege);

        computerCollege.add(new Department("软件工程",  "软件工程"));
        computerCollege.add(new Department("计算机科学与技术",  "计算机科学与技术"));
        computerCollege.add(new Department("物联网",  "物联网"));

        infoEngineerCollege.add(new Department("通信工程", "通信工程"));
        infoEngineerCollege.add(new Department("信息工程", "信息工程"));

        university.print();

    }
}

外观模式

外观模式:也叫过程模式,此模式定义了一个高层接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节。Mybatis框架中有使用到外观模式。

家庭影院需求:
DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机。要求完成使用家庭影院的功能,其过程为:
直接用遥控器统筹各设备开关
开爆米花机
放下屏幕
开投影仪
开音响
开DVD
去拿爆米花
调暗灯光
播放
观影结束后,关闭各种设备
package com.tensoflow.designPattern.structure.facade.improve1;

public class Client {
    public static void main(String[] args) {
        HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();
        homeTheaterFacade.ready();
        System.out.println("==================");
        homeTheaterFacade.end();
    }
}

享元模式

享元模式能够解决重复对象的内存浪费问题,当系统中有大量相似对象,需要缓冲池时。不需要总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率。享元模式经典的应用场景就是池技术如String常量池、数据库连接池、线程池等。

小型的外包项目,给客户A做一个产品展示网站,客户A的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求都有些不同。
1. 有客户要求以新闻的形式发布
2. 有客户要求以博客的形式发布
3. 有客户希望以微信公众号的形式发布
package com.tensoflow.designPattern.structure.flyweight.improve1;

public class Client {
    public static void main(String[] args) {
        User user = new User();
        user.setName("张三");

        WebSiteFactory factory = new WebSiteFactory();

        WebSite webSite1 = factory.getWebSiteCategory("新闻");
        webSite1.use(user);

        WebSite webSite2 = factory.getWebSiteCategory("博客");
        webSite2.use(user);

        WebSite webSite3 = factory.getWebSiteCategory("微信公众号");
        webSite3.use(user);

        WebSite webSite4 = factory.getWebSiteCategory("博客");
        webSite4.use(user);

        System.out.println("网站分类总数: " + factory.getWebSiteCount());
    }
}

代理模式

代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。这样做的好处是可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。代理模式有静态代理、动态代理、和CGLIB代理

静态代理

代理对象和被代理对象都要实现一个接口,如果接口增加了一个方法,则代理对象和被代理对象都需要去实现。

package com.tensoflow.designPattern.structure.proxy.staticProxy;

public class Client {
    public static void main(String[] args) {
        TeacherDao teacherDao = new TeacherDao();

        TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
        teacherDaoProxy.teach();
    }
}

动态代理

动态代理也叫JDK代理或者接口代理。代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理。代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。

package com.tensoflow.designPattern.structure.proxy.dynamic;

public class Client {
    public static void main(String[] args) {
        // 创建被代理对象
        ITeacherDao target = new TeacherDao();

        // 给被代理对象创建代理对象
        ITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(target).getProxyInstance();

        // 通过代理对象调用方法
        proxyInstance.teach();

    }
}

CGLIB代理

静态代理和JDK代理都要求被代理对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用被代理对象子类来实现代理。如果被代理对象需要实现接口用JDK代理,目标对象不需要实现接口用CGLIB代理。其底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。被代理的类不能用final修饰。被代理对象的方法如果为final/static,那么不会执行目标对象额外的业务方法。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>3.3.0</version>
</dependency>

行为型模式

模板方法模式

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

需求
1. 制作豆浆流程 选材--》添加配料--》侵泡--》豆浆机打碎
2. 通过添加不同的配料制作出不同口味的豆浆
3. 选材、侵泡和豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的
package com.tensoflow.designPattern.behavior.template;

public class Client {
    public static void main(String[] args) {
        // 制作红豆豆浆
        SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();
        redBeanSoyaMilk.make();

        // 制作花生豆浆
        SoyaMilk pennutSoyaMilk = new PennutSoyaMilk();
        pennutSoyaMilk.make();
    }
}

命令模式

需求
1. 买了一套智能家电,有照明灯、风扇、冰箱、洗衣机。我们只要在手机上安装App就可以控制这些家电。
2. 这些家电来自不同厂家,不想针对每一种家电都安装一个App分别控制,只希望只要一个App就可以控制全部智能家电。
package com.tensoflow.designPattern.behavior.command.improver1;

import java.rmi.Remote;

public class Client {
    public static void main(String[] args) {
        // 创建电灯对象
        LightReceiver lightReceiver = new LightReceiver();

        // 创建电灯打开的命令
        LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);

        // 创建电灯关闭的命令
        LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);

        // 创建遥控器
        RemoteController remoteController = new RemoteController();

        // 给遥控器设置命令
        remoteController.setCommand(0, lightOnCommand, lightOffCommand);

        // 打开电灯
        remoteController.onButtonWasPushed(0);

        // 关闭电灯
        remoteController.offButtonWasPushed(0);

        // 撤职操作
        remoteController.undoButtonWasPushed();
    }
}

访问者模式

访问者模式:封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

需求:
将观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价。
package com.tensoflow.designPattern.behavior.visitor.improve1;

public class Client {
    public static void main(String[] args) {
        ObjectStructure objectStructure = new ObjectStructure();

        objectStructure.attach(new Man());
        objectStructure.attach(new Woman());

        Success success = new Success();
        objectStructure.display(success);

        Fail fail = new Fail();
        objectStructure.display(fail);
    }
}

迭代器模式

迭代器模式:提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示。

需求:
编写程序展示一个学校院系结构:要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。
package com.tensoflow.designPattern.behavior.iterator.improve1;

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

public class Client {
    public static void main(String[] args) {
        List<College> list = new ArrayList<>();

        ComputerCollege computerCollege = new ComputerCollege();
        InfoCollege infoCollege = new InfoCollege();

        list.add(computerCollege);
        list.add(infoCollege);

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

观察者模式

需求:
1.气象站可以将每天测量到的温度、湿度、气压等以公告的形式发布出去
2.需要设计开放型API,便于其它第三方也能接入气象站获取数据
3.提供温度、气压、湿度的接口
4.测量数据更新时,要能实时通知给第三方
package com.tensoflow.designPattern.behavior.observer.improve1;

public class Client {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();

        // 创建观察者
        CurrentConditions currentConditions = new CurrentConditions();
        BaiduSite baiduSite = new BaiduSite();

        // 注册观察者
        weatherData.registerObserver(currentConditions);
        weatherData.registerObserver(baiduSite);

        weatherData.setData(10f, 100f, 30.3f);
    }
}

中介者模式

智能家庭需求:
1.智能家庭包括各种设备:闹钟、咖啡机、电视机、窗帘等
2.主人要看电视时,各个设备可以协同工作,自动完成看电视的准备工作。
3.流程为:闹铃响起 --》咖啡机开始做咖啡 --》 窗帘自动落下 --》 电视机开始播放
package com.tensoflow.designPattern.behavior.mediator.improve1;

public class Client {
    public static void main(String[] args) {
        Mediator mediator = new ConcreteMediator();

        Alarm alarm = new Alarm(mediator, "alarm");
        CoffeeMachine coffeeMachine = new CoffeeMachine(mediator, "coffeeMachine");
        Curtains curtains = new Curtains(mediator, "curtains");
        Tv tV = new Tv(mediator, "Tv");

        alarm.sendAlarm(0);
        coffeeMachine.finishCoffee(0);
        alarm.sendAlarm(1);
    }
}

备忘录模式

需求:
游戏角色有状态(state),在大战Boss前保存自身的状态,当大战Boss后,从备忘录对象恢复到大战前的状态。
package com.tensoflow.designPattern.behavior.memento.improve1;

public class Client {
    public static void main(String[] args) {
        Originator originator = new Originator();
        originator.setState("状态1");

        Caretaker caretaker = new Caretaker();
        // 保存 状态1
        caretaker.add(originator.saveStateMemento());
        System.out.println(originator.getState());

        originator.setState("状态2");
        // 保存 状态2
        caretaker.add(originator.saveStateMemento());
        System.out.println(originator.getState());

        originator.setState("状态3");
        // 保存 状态3
        caretaker.add(originator.saveStateMemento());
        System.out.println(originator.getState());

        // 恢复到状态2
        originator.setState(caretaker.get(1).getState());
        System.out.println(originator.getState());

    }
}

解释器模式

需求:
通过解释器模式计算a + b - c的值
package com.tensoflow.designPattern.behavior.interpreter.improve1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;

public class Client {
    public static void main(String[] args) throws IOException {
        String expStr = getExpStr();
        HashMap<String, Integer> var = getValue(expStr);
        Calculator calculator = new Calculator(expStr);
        System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
    }

    // 获得表达式
    public static String getExpStr() throws IOException {
        System.out.print("请输入表达式:");
        return (new BufferedReader(new InputStreamReader(System.in))).readLine();
    }

    // 获得值映射
    public static HashMap<String, Integer> getValue(String expStr) throws IOException {
        HashMap<String, Integer> map = new HashMap<>();
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        for (char ch : expStr.toCharArray()) {
            if (Character.isLetter(ch) && !map.containsKey(String.valueOf(ch))) {
                System.out.print("请输入 " + ch + " 的值:");
                String value = br.readLine();
                map.put(String.valueOf(ch), Integer.parseInt(value));
            }
        }
        return map;
    }

}

状态模式

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

需求:
1.假如每参加一次这个活动要扣除用户50积分,中奖概率是10%
2.奖品数量固定,抽完就不能抽奖
3.活动有四个状态:可以抽奖、不能抽奖、发放奖品、奖品领完
package com.tensoflow.designPattern.behavior.state.improve1;

public class Client {
    public static void main(String[] args) {
        // 创建活动对象,奖品池有1个奖品
        RaffleActivity activity = new RaffleActivity(1);

        // 连续抽奖
        for (int i = 0; i < 100; i++) {
            System.out.println("----------第" + (i + 1) + "次抽奖----------");
            // 参加抽奖,第一步点击扣除积分
            activity.deductMoney();

            // 第二步抽奖
            activity.raffle();
        }

    }
}

策略模式

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

需求:
1.有各种鸭子(野鸭、北京鸭、水鸭),鸭子有各种行为比如叫、飞行等
2.显示鸭子信息
package com.tensoflow.designPattern.behavior.strategy.improve1;

public class Client {
    public static void main(String[] args) {
        WildDuck wildDuck = new WildDuck();
        wildDuck.fly();

        PekingDuck pekingDuck = new PekingDuck();
        pekingDuck.fly();

        ToyDuck toyDuck = new ToyDuck();
        toyDuck.fly();
    }
}

职责链模式

职责链模式:为请求创建一个接受对象的链。通常每个接受者都包含对另一个接受者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接受者,以此类推。

学校OA系统的采购审批项目:需求是
1.采购员采购教学器材
2.如果金额小于等于5000,由教学主任审批
3.如果金额小于等于10000,由院长审批
4.如果金额小于等于30000,由副校长审批
5.如果金额超过30000以上,由校长审批
package com.tensoflow.designPattern.behavior.responsibilityChain.improve1;

public class Client {
    public static void main(String[] args) {
        // 创建请求
        PurchaseRequest purchaseRequest = new PurchaseRequest(1, 1000, 1);

        // 创建审批人
        DepartmentApprover departmentApprover = new DepartmentApprover("张主任");
        CollegeApprover collegeApprover = new CollegeApprover("李院长");
        ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校长");
        SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("刘校长");

        // 设置审批流程
        departmentApprover.setApprover(collegeApprover);
        collegeApprover.setApprover(viceSchoolMasterApprover);
        viceSchoolMasterApprover.setApprover(schoolMasterApprover);
        schoolMasterApprover.setApprover(departmentApprover);

        departmentApprover.processRequest(purchaseRequest);

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