java中设计模式的使用(持续更新中)

news2024/11/20 4:43:07

概述

设计模式的目的:编写软件过程中,程序员面临着来自耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性等多方面的挑战,设计模式是为了让程序(软件),具有更好的

  • 代码重用性(相同功能的代码,不用多次编写)
  • 可读性(编程规范性,便于其他程序员的阅读和理解)
  • 可扩展性(当需要增加新的功能时,非常的方便,也叫可维护性)
  • 可靠性(当我们增加新的功能后,对原来的功能没用影响)
  • 使程序呈现高内聚,低耦合的特性

设计模式的七大原则:

  1. 单一职责原则
  • 对类来说,及一个类应该只负责一项职责。如类A负责两个不同的职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2。(例如一个UserDao,同时操作user表和order表,因此需要将UserDao拆分为UserDao和OrderDao)
  • 单一职责原则注意事项和细节
    • 降低类的复杂度,一个类只负责一项职责
    • 提高类的可读性,可维护性
    • 降低变更引起的风险
    • 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则
  1. 接口隔离原则
  • 客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
  1. 依赖倒转原则
  • 高层模块不应该依赖底层模块,二者都应该依赖其抽象
  • 抽象不应该依赖细节,细节应该依赖抽象
  • 依赖倒转的中心思想是面向接口编程
  • 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的框架要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现
  • 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
  1. 里氏替换原则
  • 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
  • 继承在给程序设计带来遍历的同时,也带来了弊端。比如使用继承会给程序带来侵入性。程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且修改后,所有设计到子类的功能都有可能产生故障。
  1. 开闭原则ocp(开闭原则,工厂模式)
  • 开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则
  • 一个软件实体如类,模块和函数应该对扩展开放(针对提供方),对修改关闭(针对使用方)用抽象构建框架,用实现扩展细节
  • 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
  • 编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则
  1. 迪米特法则
  • 一个对象应该对其他对象保持最少的了解
  • 类与类关系越密切,耦合度越大
  • 迪米特法则,又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么的复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外泄漏任务信息
  • 迪米特法则还有哥更简单的定义:只与直接的朋友通信。(直接朋友:每个对象都会与其他对象有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部)
  1. 合成复用原则
  • 原则是尽量使用合成/聚合的方式,而不是使用继承

设计模式分为三种类型

  • 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
  • 结构型模式:适配器模式、桥接模式、装饰者模式、组合模式、外观模式、享元模式、代理模式
  • 行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、责任链模式

1.单例模式

所谓类得单例设计模式、就是采取一定得方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得对象实例的方法(一般静态方法)。

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

单例模式有八种方式:

  • 饿汉式(静态常量)
  • 饿汉式(静态代码块)
  • 懒汉式(线程不安全)
  • 懒汉式(线程安全,同步方法)
  • 懒汉式(线程安全,同步代码块)
  • 双重检查
  • 静态内部类
  • 枚举

步骤:构造方法私有化,提供一个公共的方法供外部获取对象

饿汉式(静态常量)

优点:在类装载的时候就完成实例化,避免了线程同步问题

缺点:从未使用过这个实例,造成内存浪费

public class Singleton {

    private static final Singleton instance = new Singleton();
    //构造方法私有化
    private Singleton(){}
    //公共方法获取实例
    public static Singleton getInstance(){
        return instance;
    }
}
饿汉式(静态代码块)
public class Singleton {

    private static Singleton instance;
    
    static {
        instance = new Singleton();
    }

    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}
懒汉式(线程不安全)

在多线程环境下,一个线程进入了if判断语句块,还未来得及往下执行,另外一个线程获得cpu的时间片往下执行,会创建多个实例。

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

解决线程不安全问题,但是效率低,当对象已经被创建后,所有线程都还是会去竞争锁,还是会去进入判断

public class Singleto {
    
    private static Singleto instance;
    
    private Singleto(){}
    
    public static synchronized  Singleto getInstance(){
        if(instance == null){
            instance = new Singleto();
        }
        return instance;
    }
}
懒汉式(线程安全,同步代码块)
public class Singleto {
    private static Singleto instance;
    
    private Singleto(){}
    
    public static  Singleto getInstance(){
        synchronized (Singleto.class) {
            if (instance == null) {
                instance = new Singleto();
            }
            return instance;
        }
    }
}
双重检查

又叫双重校验锁,解决线程安全问题,同时解决竞争锁性能问题,并且满足懒加载

public class Singleto {
    private static Singleto instance;

    private Singleto() {
    }

    public static Singleto getInstance() {

        if (instance == null) {
            synchronized (Singleto.class) {
                if (instance == null) {
                    instance = new Singleto();
                }
            }
        }
        return instance;

    }
}
静态内部类

SingletonHolder在Singleto装载时不会被加载,在调用getInstance时才被加载,达到懒加载模式。并且jvm加载类时是线程安全的。

public class Singleto {
    
    private Singleto(){
        
    }
    
    private static class SingletonHolder{
        private static final Singleto INSTANCE = new Singleto();
    }
    
    public static Singleto getInstance(){
        return SingletonHolder.INSTANCE;
    } 
}
枚举
public enum Singleton {
    INSTANCE;
    public void doSomething() {

    }
}

2.工厂模式

  • 工厂模式的意义:将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性
  • 三种工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
  • 创建对象实例时,不要直接new,而是吧这个new类的动作放在一个工厂的方法中,并返回,变量不要直接持有具体类的引用。
  • 不要让类继续具体类,而是继承抽象类或者是实现接口
  • 不要覆盖基类中已经实现的方法
2.1简单工厂模式

简单工厂模式又叫静态工厂模式,原理就是将需要创建的对象抽取到一个工厂类中。使用的时候通过调用工厂获取对象,这样可以将对象的创建等细节抽取到一处,发生变更时只用更改工厂类即可,满足ocp原则。

披萨基类

public abstract class Pizza {
    String name;

    public abstract void prepare();
    public abstract void bake();
    public abstract void cut();
    public abstract void box();
}

创建两种披萨

/**
 * 芝士披萨
 *
 * @author 
 * @date 2024-11-06
 */
public class CheesePizza extends Pizza {

    public CheesePizza() {
        super.name = "奶酪披萨";
    }

    public void prepare() {
        System.out.println("开始制作" + name);
    }

    public void bake() {
        System.out.println(name + "烘烤中");
    }

    public void cut() {
        System.out.println(name + "切割中");
    }

    public void box() {
        System.out.println(name + "打包中");
    }
}
/**
 * 牛肉披萨
 *
 * @author 
 * @date 2024-11-06
 */
public class BeefPizza extends Pizza{
    public BeefPizza() {
        super.name = "牛肉披萨";
    }

    public void prepare() {
        System.out.println("开始制作" + name);
    }

    public void bake() {
        System.out.println(name + "烘烤中");
    }

    public void cut() {
        System.out.println(name + "切割中");
    }

    public void box() {
        System.out.println(name + "打包中");
    }
}

创建一个披萨订单

/**
 * 订一个披萨
 *
 * @author 
 * @date 2024-11-06
 */
public class OrderPizza {
    String type;

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

    public void submitOrder() {
        String orderType;
        orderType = getType();
        Pizza pizza = PizzaFactory.createPizza(orderType);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
    }

    private String getType() {
        return this.type;
    }
}

创建披萨工厂

/**
 * 披萨工厂
 *
 * @author 
 * @date 2024-11-06
 */
public class PizzaFactory {
    public static Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if (orderType.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (orderType.equals("beef")) {
            pizza = new BeefPizza();
        } else {
            System.out.println("没有该类型");
        }
        return pizza;
    }
}

披萨店订购披萨

/**
 * 披萨店
 *
 * @author 
 * @date 2024-11-06
 */
public class PizzaStore {
    public static void main(String[] args) {
        OrderPizza orderPizza = new OrderPizza("beef");
        orderPizza.submitOrder();
    }
}

这样的好处就是扩展性强,以后有其他的披萨类型,只用创建一个新的披萨类,然后再工厂中添加新的披萨,其他使用工厂的创建即可,不用关心对象的创建。不使用工厂模式,新添加一个类型时,所有创建披萨的地方都要更改。

2.2工厂方法模式

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

披萨基类

public abstract class Pizza {
    String name;

    public abstract void prepare();
    public abstract void bake();
    public abstract void cut();
    public abstract void box();
}

不同区域和口味的披萨

public class BJBeefPizza extends Pizza {
    public BJBeefPizza() {
        super.name = "北京牛肉披萨";
    }

    public void prepare() {
        System.out.println("开始制作" + name);
    }

    public void bake() {
        System.out.println(name + "烘烤中");
    }

    public void cut() {
        System.out.println(name + "切割中");
    }

    public void box() {
        System.out.println(name + "打包中");
    }
}

public class BJCheesePizza extends Pizza {

    public BJCheesePizza() {
        super.name = "北京奶酪披萨";
    }

    public void prepare() {
        System.out.println("开始制作" + name);
    }

    public void bake() {
        System.out.println(name + "烘烤中");
    }

    public void cut() {
        System.out.println(name + "切割中");
    }

    public void box() {
        System.out.println(name + "打包中");
    }
}


public class CDBeefPizza extends Pizza {
    public CDBeefPizza() {
        super.name = "成都牛肉披萨";
    }

    public void prepare() {
        System.out.println("开始制作" + name);
    }

    public void bake() {
        System.out.println(name + "烘烤中");
    }

    public void cut() {
        System.out.println(name + "切割中");
    }

    public void box() {
        System.out.println(name + "打包中");
    }
}


public class CDCheesePizza extends Pizza {

    public CDCheesePizza() {
        super.name = "成都奶酪披萨";
    }

    public void prepare() {
        System.out.println("开始制作" + name);
    }

    public void bake() {
        System.out.println(name + "烘烤中");
    }

    public void cut() {
        System.out.println(name + "切割中");
    }

    public void box() {
        System.out.println(name + "打包中");
    }
} 

抽象工厂方法类

public abstract class OrderPizza {
    protected String orderType;

    abstract Pizza createPizza(String orderType);

    public OrderPizza(String type) {
        orderType = type;
    }

    public void submitOrder() {
        Pizza pizza = createPizza(orderType);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
    }
}

具体的工厂实现

public class BJOrderPizza extends OrderPizza {
    public BJOrderPizza(String type) {
        super(type);
    }

    Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if (orderType.equals("cheese")) {
            pizza = new BJCheesePizza();
        } else if (orderType.equals("beef")) {
            pizza = new BJBeefPizza();
        }
        return pizza;
    }
}


public class CDOrderPizza extends OrderPizza{
    public CDOrderPizza(String type) {
        super(type);
    }

    Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if (orderType.equals("cheese")) {
            pizza = new CDCheesePizza();
        } else if (orderType.equals("beef")) {
            pizza = new CDBeefPizza();
        }
        return pizza;
    }
}

演示


public class PizzaStore {
    public static void main(String[] args) {
        new BJOrderPizza("cheese").submitOrder();
        new CDOrderPizza("beef").submitOrder();
    }
}
2.3抽象工厂模式
  • 抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
  • 抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合
  • 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)
  • 将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用相应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

抽象工厂

public interface AbsFactory {
    Pizza createPizza(String type);
}

具体的工厂

public class BJFactory implements AbsFactory{
    public Pizza createPizza(String type) {
        Pizza pizza = null;
        if (type.equals("cheese")) {
            pizza = new BJCheesePizza();
        } else if (type.equals("beef")) {
            pizza = new BJBeefPizza();
        }
        return pizza;
    }
}

public class CDFactory implements AbsFactory{
    public Pizza createPizza(String type) {
        Pizza pizza = null;
        if (type.equals("cheese")) {
            pizza = new CDCheesePizza();
        } else if (type.equals("beef")) {
            pizza = new CDBeefPizza();
        }
        return pizza;
    }
}

工厂的使用

public class OrderPizza {
    AbsFactory factory;

    public OrderPizza(AbsFactory factory){
        this.factory = factory;
    }
    public void submitOrder(String orderType) {
        Pizza pizza = factory.createPizza(orderType);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
    }
}

结果展示


public class PizzaStore {
    public static void main(String[] args) {
        OrderPizza orderPizza = new OrderPizza(new BJFactory());
        orderPizza.submitOrder("cheese");
    }
}

3.原型模式

  • 原型模式(prototype)是指:用原型实例指定创建对象的种类。并且通过拷贝这些原型,创建新的对象。
  • 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需指定如何创建的细节
  • 工作原理是通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即,对象.clone()
3.1代码实现

原型类实现Cloneable接口,覆写clone方法

public class Sheep implements Cloneable{
    private String name;
    private int age;
    private String color;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {}

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
3.2深拷贝和浅拷贝问题

浅拷贝

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象
  • 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
  • 浅拷贝则是使用默认的clone()方法来实现

深拷贝

  • 复制对象的所有基本数据类型的成员变量值
  • 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
  • 深拷贝实现方式1:重写clone方法来实现深拷贝
  • 深拷贝实现方式2:通过对象序列化实现深拷贝(推荐)
package com.demo.design_pattern.prototype;

import java.io.Serializable;

public class Beef implements Cloneable, Serializable {
    private String name;

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

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Beef{" +
                "name='" + name + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
package com.demo.design_pattern.prototype;

import java.io.*;

public class Sheep implements Cloneable , Serializable {
    private String name;
    private int age;
    private String color;

    private Beef friend;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {}

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public Beef getFriend() {
        return friend;
    }

    public void setFriend(Beef friend) {
        this.friend = friend;
    }

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

    /*
    * 方式一:完成深拷贝
    * */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object clone = super.clone();
        Sheep sheep = (Sheep) clone;
        sheep.friend = (Beef) this.friend.clone();
        return clone;
    }
    /*
     * 方式二:完成深拷贝
     * */
    public Object deepClone(){
        Sheep sheep = null;
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis =null;
        ObjectInputStream ois =null;
        //创建流对象
        try {
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            // 序列号,写入当前对象的二进制流
            oos.writeObject(this);

            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            // 反序列化,从二进制流产生当前对象
            sheep = (Sheep) ois.readObject();
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                assert bos != null;
                bos.close();
                assert oos != null;
                oos.close();
                assert bis != null;
                bis.close();
                assert ois != null;
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sheep;
    }
}

代码演示


  Sheep sheep = new Sheep("肖恩", 12, "白色");
        sheep.setFriend(new Beef("小黑"));
//        Sheep sheep1 = (Sheep) sheep.clone();
        Sheep sheep1 = (Sheep) sheep.deepClone();
        System.out.println(sheep1.getFriend() == sheep.getFriend());
        System.out.println(sheep1);
        System.out.println(sheep);

原型模式的注意事项和细节:

  • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能提高效率
  • 不用重新初始化对象,而是动态地获得运行时的状态
  • 如果原始对象发生改变(增加或减少属性),其它克隆对象也会发生相应的变化,而无需修改代码
  • 缺点:需要为每一个类配备一个克隆方法,对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了opc原则。

4.建造者模式

4.1概念
  • 建造者模式(Builder Pattern)又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象
  • 建造者模式使一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
4.2核心角色
  • Product(产品角色):一个具体的产品对象
  • Builder(抽象建造者):创建一个Product对象的各个部件指定的接口/抽象类。
  • ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件。
  • Director(指挥者):构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要又两个作用,一是:隔离了客户与对象的生产过程,二是负责控制产品对象的生产过程。
4.3代码实现

4.3.1建造的产品
/**
 * 房子 --- 对应product
 *
 * @author 
 * @date 2024-11-11
 */
public class House {
    private String basic;
    private String wall;
    private String roofed;

    public String getBasic() {
        return basic;
    }

    public void setBasic(String basic) {
        this.basic = basic;
    }

    public String getWall() {
        return wall;
    }

    public void setWall(String wall) {
        this.wall = wall;
    }

    public String getRoofed() {
        return roofed;
    }

    public void setRoofed(String roofed) {
        this.roofed = roofed;
    }

}
4.3.2抽象建造者-定义建造的方法
/**
 * 房屋建造者 --- 对应抽象的建造者
 *
 * @author 
 * @date 2024-11-11
 */
public abstract class HouseBuilder {
    protected House house = new House();

    /*
    * 定义房子建造的步骤
    * */
    public abstract void buildBasic();

    public abstract void buildWall();

    public abstract void roofed();

    public House buildHouse() {
        return house;
    }
}
4.3.3具体的建造执行者--实现具体的建造方法
public class HigBuilding extends HouseBuilder{
    @Override
    public void buildBasic() {
        System.out.println("高楼房子打地基50米");
        house.setBasic("高楼房子打地基50米");
    }

    @Override
    public void buildWall() {
        System.out.println("高楼房子砌墙80cm");
        house.setWall("高楼房子砌墙80cm");
    }

    @Override
    public void roofed() {
        System.out.println("高楼房子封顶");
        house.setRoofed("高楼房子封顶");
    }
}
4.3.4指挥者 --- 定义建造的流程(控制方法的执行流程)
/**
 * 房子建造指挥者
 *
 * @author 
 * @date 2024-11-11
 */
public class HouseDirector {
    private HouseBuilder houseBuilder;

    public HouseDirector(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

     /**
     * 建造房子,定义产品的创建过程
     *
     * @return 
     */
    public House constructHouse() {
        // 调用建造者中的方法,完成产品的创建
        houseBuilder.buildBasic();
        houseBuilder.buildWall();
        houseBuilder.roofed();
        return houseBuilder.buildHouse();
    }
}
4.3.5使用
public class Client {
    public static void main(String[] args) {
        HigBuilding higBuilding = new HigBuilding();
        HouseDirector houseDirector = new HouseDirector(higBuilding);
        House house = houseDirector.constructHouse();
        System.out.println(house);
    }
}
4.3.6建造者模式的注意事项和细节
  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式

抽象工厂模式 vs 建造者模式

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

5.适配器模式

5.1概述
  • 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
  • 适配器模式属于结构型模式
  • 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
5.2工作原理
  • 将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容
  • 从用户的角度看不到被适配者,是解耦的
  • 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
  • 用户收到反馈结果,感觉只是和目标接口交互
5.3类适配器模式

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

/**
 * 电源220v
 * src
 *
 * @author
 * @date 2024-11-19
 */
public class Voltage220V {
    public int output220V(){
        int src = 220;
        System.out.println("电压="+src+"伏");
        return src;
    }
}
/**
 * 5v 电压接口
 * dist
 *
 * @author 
 * @date 2024-11-19
 */
public interface IVoltage5V {
    int output5V();
}
/**
 * 电压适配器
 *
 * @author
 * @date 2024-11-19
 */
public class VoltageAdapter extends Voltage220V implements IVoltage5V {

    @Override
    public int output5V() {
        int output220V = super.output220V();
        int dst = output220V / 44;
        System.out.println("适配完成,电压="+dst+"伏");
        return dst;
    }
}
/**
 * 电话
 *
 * @author
 * @date 2024-11-19
 */
public class Phone {
    public void charge(IVoltage5V iVoltage5V){
        if(iVoltage5V.output5V()==5){
            System.out.println("手机充电中。。。");
        }else{
            System.out.println("电压不匹配,无法充电");
        }
    }
}
public class Client {
    public static void main(String[] args) {
        VoltageAdapter adapter = new VoltageAdapter();
        new Phone().charge(adapter);
    }
}

类适配器模式注意事项和细节

  • java是单继承机制,所以类适配器需要继承src类这一点算是一个缺点,因为这要求dst必须是接口,有一定局限性
  • src类的方法在Adapter中都会暴露出来,也增加使用的成本
  • 由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵活性增强了。
5.4对象适配器模式
  • 基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而是持有src类的实例,以解决兼容性的问题。即:持有src类,实现dst类接口,完成src->dst的适配
  • 根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系。
  • 对象适配器模式是适配器模式常用的一种
/**
 * 电源220v
 * src
 *
 * @author
 * @date 2024-11-19
 */
public class Voltage220V {
    public int output220V(){
        int src = 220;
        System.out.println("电压="+src+"伏");
        return src;
    }
}
/**
 * 5v 电压接口
 * dist
 *
 * @author
 * @date 2024-11-19
 */
public interface IVoltage5V {
    int output5V();
}
/**
 * 电话
 *
 * @author
 * @date 2024-11-19
 */
public class Phone {
    public void charge(IVoltage5V iVoltage5V){
        if(iVoltage5V.output5V()==5){
            System.out.println("手机充电中。。。");
        }else{
            System.out.println("电压不匹配,无法充电");
        }
    }
}
/**
 * 电压适配器
 *
 * @author
 * @date 2024-11-19
 */
public class VoltageAdapter implements IVoltage5V {
    private Voltage220V voltage220V;

    public VoltageAdapter(Voltage220V voltage220V) {
        this.voltage220V = voltage220V;
    }

    @Override
    public int output5V() {
        int output220V = voltage220V.output220V();
        int dst = output220V / 44;
        System.out.println("适配完成,电压="+dst+"伏");
        return dst;
    }
}
public class Client {
    public static void main(String[] args) {
        Voltage220V voltage220V = new Voltage220V();
        IVoltage5V voltage5V = new VoltageAdapter(voltage220V);
        new Phone().charge(voltage5V);
    }
}

对象适配器模式注意事项和细节

  • 对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用组合代替继承,所以它解决了类适配器必须继承src的局限性问题,也不再要求dst必须是接口。
  • 使用成本更低,更灵活
5.5接口适配器模式
  • 适配器模式或缺省适配器模式
  • 当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供给一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
  • 适用于一个接口不想使用其所有的方法的情况

举例:springMVC中的拦截器实现,HandlerInterceptor和HandlerInterceptorAdapter

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2243806.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode 有效的数独

这段代码解决的是 验证一个数独是否有效 的问题,其算法思想是基于 规则校验和状态记录。具体思想如下: 算法思想 核心目标: 检查每个数字在 同一行、同一列 和 同一个 3x3 子格 中是否重复。 状态记录: 使用 3 个布尔二维数组分别…

群控系统服务端开发模式-应用开发-前端文件格式功能开发

一、添加视图 在根目录下src文件夹下views文件夹下param文件夹下filedoc文件夹下&#xff0c;新建index.vue&#xff0c;代码如下 <template><div class"app-container"><div class"filter-container" style"float:left;">&l…

可认证数据资产合约标准协议(CMIDA-1)意见征集

标准背景 数据资产具备多维度的属性&#xff0c;涵盖行业特性、状态信息、资产类型、存储格式等。数据资产在不同流通主体之间可理解、可流通、可追溯、可信任的重要前提之一是存在统一的标准&#xff0c;缺失统一的标准&#xff0c;数据混乱冲突、一数多源、多样多类等问题将…

大数据-227 离线数仓 - Flume 自定义拦截器(续接上节) 采集启动日志和事件日志

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; Java篇开始了&#xff01; 目前开始更新 MyBatis&#xff0c;一起深入浅出&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff0…

Solana应用开发常见技术栈

编程语言 Rust Rust是Solana开发中非常重要的编程语言。它具有高性能、内存安全的特点。在Solana智能合约开发中&#xff0c;Rust可以用于编写高效的合约代码。例如&#xff0c;Rust的所有权系统可以帮助开发者避免常见的内存错误&#xff0c;如悬空指针和数据竞争。通过合理利…

redis类型介绍

1. 字符串&#xff08;String&#xff09;&#xff1a; • 简介&#xff1a;最基础的数据类型&#xff0c;可以存储任何形式的字符串&#xff0c;包括文本数据和数字数据。 • 常用操作&#xff1a;SET、GET、INCR、DECR等。 2. 列表&#xff08;List&#xff09;&#xff1a; …

Pytest-Bdd-Playwright 系列教程(10):配置功能文件路径 优化场景定义

Pytest-Bdd-Playwright 系列教程&#xff08;10&#xff09;&#xff1a;配置功能文件路径 & 优化场景定义 前言一、功能文件路径的配置1.1 全局设置功能文件路径1.2. 在场景中覆盖路径 二、避免重复输入功能文件名2.1 使用方法2.2 functools.partial 的背景 三、应用场景总…

HarmonyOs鸿蒙开发实战(17)=>沉浸式效果第二种方案一组件安全区方案

1.沉浸式效果的目的 开发应用沉浸式效果主要指通过调整状态栏、应用界面和导航条的显示效果来减少状态栏导航条等系统界面的突兀感&#xff0c;从而使用户获得最佳的UI体验。 2.组件安全区方案介绍 应用在默认情况下窗口背景绘制范围是全屏&#xff0c;但UI元素被限制在安全区内…

构建安全的数据库环境:群晖NAS安装MySQL和phpMyAdmin详细步骤

文章目录 前言1. 安装MySQL2. 安装phpMyAdmin3. 修改User表4. 本地测试连接MySQL5. 安装cpolar内网穿透6. 配置MySQL公网访问地址7. 配置MySQL固定公网地址8. 配置phpMyAdmin公网地址9. 配置phpmyadmin固定公网地址 前言 本文将详细讲解如何在群晖NAS上安装MySQL及其数据库管理…

【c++丨STL】list的使用

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C、STL 目录 前言 list简介 一、list的默认成员函数 构造函数(constructor) 析构函数 赋值重载 二、list的迭代器接口 迭代器的功能分类 三、list的容量…

如何编译 Cesium 源码

如何编译 Cesium 源码 Cesium 是一个开源的 JavaScript 库&#xff0c;用于构建 3D 地球和地图应用程序。它提供了一套强大的 API 和工具&#xff0c;使开发者能够创建丰富的地理空间应用。本文将指导您如何从 GitHub 下载 Cesium 源码&#xff0c;并在本地进行编译。 TilesB…

实验5:网络设备发现、管理和维护

实验5&#xff1a;网络设备发现、管理和维护 实验目的及要求&#xff1a; 通过实验&#xff0c;掌握Cisco 路由器和交换机的IOS配置管理。自动从NTP服务器获取时间信息。能够利用TFTP服务器实现路由器和交换机配置文件的备份和恢复。同时验证CDP协议和LLDP协议的网络参数。完…

全志T113双核异构处理器的使用基于Tina Linux5.0——RTOS编译开发说明

3、RTOS编译开发说明 3.1、RTOS SDK与TinaLinux开发环境 RTOS SDK相关代码已集成到Tina Linux开发环境&#xff0c;Tina Linux开发环境下的rtos子目录即为RTOS开发环境。 ├──brandy ├──bsp ├──build ├──buildroot ├──build.sh >build/top_build.sh ├──…

汽车资讯新篇章:Spring Boot技术启航

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-04

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-04 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-04目录1. Alopex: A Computational Framework for Enabling On-Device Function Calls with LLMs摘要&#xff1a;研究背景&…

细说STM32单片机DMA中断收发RTC实时时间并改善其鲁棒性的方法

目录 一、DMA基础知识 1、DMA简介 (1)DMA控制器 (2)DMA流 (3)DMA请求 (4)仲裁器 (5)DMA传输属性 2、源地址和目标地址 3、DMA传输模式 4、传输数据量的大小 5、数据宽度 6、地址指针递增 7、DMA工作模式 8、DMA流的优先级别 9、FIFO或直接模式 10、单次传输或突…

review-消息中间件MQ

RabbitMQ RabbitMQ&#xff0c;作为当今流行的开源消息代理软件&#xff0c;以其卓越的可靠性、灵活性和易用性在微服务架构和分布式系统中扮演着至关重要的角色。它不仅能够确保消息在不同系统组件间的高效传递&#xff0c;还能通过其高级消息队列协议&#xff08;AMQP&#x…

使用 .NET 创建新的 WPF 应用

本教程介绍如何使用 Visual Studio 创建新的 Windows Presentation Foundation &#xff08;WPF&#xff09; 应用。 使用 Visual Studio&#xff0c;可以向窗口添加控件以设计应用的 UI&#xff0c;并处理这些控件中的输入事件以与用户交互。 在本教程结束时&#xff0c;你有一…

【青牛科技】视频监控器应用

1、简介&#xff1a; 我司安防产品广泛应用在视频监控器上&#xff0c;产品具有性能优良&#xff0c;可 靠性高等特点。 2、图示&#xff1a; 实物图如下&#xff1a; 3、具体应用&#xff1a; 标题&#xff1a;视频监控器应用 简介&#xff1a;视频监控器工作原理是光&#x…

Android 项目依赖库无法找到的解决方案

目录 错误信息解析 解决方案 1. 检查依赖版本 2. 检查 Maven 仓库配置 3. 强制刷新 Gradle 缓存 4. 检查网络连接 5. 手动下载依赖 总结 相关推荐 最近&#xff0c;我在编译一个 Android 老项目时遇到了一个问题&#xff0c;错误信息显示无法找到 com.gyf.immersionba…