设计模式

统一建模语言 Unified Modeling Language,UML

这里主要使用 UML 绘制类图

  • Dependency 依赖、使用
  • Association 关联
  • Generalization 泛化、继承
  • Realization 实现
  • Aggregation 聚合(关联的一种)
  • Composite 组合(关联的一种)

依赖

只要在类中用到了对方,那么就存在依赖关系,如:对于A依赖B

  • B类是A类的成员属性
  • B类是A类方法的返回类型
  • B类是A类方法接收的参数类型
  • B类在A类方法中使用到

泛化

是一种依赖关系的特例,泛化关系就是继承关系如:

对于B是A的泛化,指的就是B类继承A类

实现

是一种依赖关系的特例,如对接口的实现

关联

是一种依赖关系的特例,是类与类之间的联系,关联具有导航性,如双向或单向的关系,多重性

  • 如B类是A类的成员属性,则称为单项关系,若A类同时是B类的成员属性,则称为双向关系
  • 如B类是A类的一个成员属性,则是单一关联,也可以是多个B类都是A类的成员属性

聚合

是一种关联关系的特例,表示的是整体和部分的关系,整体与部分可以分开

  • 如B、C类是A类的属性和方法,但B、C类可以和A类去除
public class Computer {
    private Mouse mouse;
    private Monitor monitor;

    public void setMouse(Mouse mouse) {
        this.mouse = mouse;
    }

    public void setMonitor(Monitor monitor) {
        this.monitor = monitor;
    }
}

组合

同聚合一样,但整体与部分不能分开

public class Computer {
    private Mouse mouse = new Mouse();
    private Monitor monitor = new Monitor();

    public void setMouse(Mouse mouse) {
        this.mouse = mouse;
    }

    public void setMonitor(Monitor monitor) {
        this.monitor = monitor;
    }
}

在本例中当 Computer 被实例化后,Mouse 和 Monitor 也同时被实例。

设计模式分类

设计模式共分为三种类型,共23种,不同资料分类和名称略有区别

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

单例模式

采取一定的方法,保证在整个软件系统中,对某个类只能存在一个对象实例 ,并且对该类只提供一个取得对象实例的方法。

单例模式共有8种方式

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

饿汉式(静态常量)

方法

  1. 构造器私有化(防止使用new)
  2. 类的内部创建对象
  3. 向外暴漏一个静态的公共方法 getInstance
  4. 代码实现
public class Single1 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); //true
    }

}

class Singleton {
    private Singleton() {

    }

    private final static Singleton instance = new Singleton();
    public static Singleton getInstance() {
        return instance;
    }
}

优缺点:

  • 写法简单,类装载时完成实例化,避免了线程同步问题
  • 如果没有使用这个实例,会造成资源浪费,没有达到 lazy loading 的效果

这种单例模式可用,但可能造成内存浪费

饿汉式(静态代码块)

方法

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

class Singleton {
    private Singleton() {
    }

    private static Singleton instance;
    static {
        instance = new Singleton();
    }
    public static Singleton getInstance() {
        return instance;
    }
}

优缺点:

与上面的静态变量类似,优缺点相同

这种单例模式可用,但可能造成内存浪费

懒汉式(线程不安全)

方法

public class Single3 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        System.out.println(instance == instance2); //true
    }
}

class Singleton {
    private static Singleton instance;
    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

优缺点:

  • 起到了Lazy loading的效果,但只能在单线程模式下使用
  • 多线程下,一个线程进入了 if (Singleton == null) 语句块,还未执行时,另一个线程也通过了这个判断语句,这是会产生多个实例

实际开发时,不可用

懒汉式(线程安全,同步方法)

方法

将 public static Singleton getInstance() 替换为

public static synchronized Singleton getInstance()

优缺点

  • 解决了线程不安全的问题
  • 效率低,每个线程在获得类的实例时,执行 getInstance 方法都要进行同步,
  • 实际上,执行一次实例化就够了,再想获得直接return

实际开发时,不建议使用

懒汉式(同步代码块)

方法

同懒汉式(线程不安全),加入 if (Singleton == null) 判断后再同步

优缺点

  • 本意是对四种实现方式改进
  • 实际上不能起到线程同步的作用,可能会产生多个实例

线程不安全,不可用

双重检查

方法

public class Single6 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        System.out.println(instance == instance2); //true
    }
}

class Singleton {
    private static volatile Singleton instance;
    private Singleton() {}

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

优缺点

  • 两次检查,线程安全
  • 实例化代码一次,再访问时,判断(singleton == null) 直接 return 对象
  • 线程安全,延迟加载,效率较高

推荐使用

静态内部类

方法

注:类加载时,静态内部类不被加载;调用时静态内部类会被加载

public class Single7 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        System.out.println(instance == instance2); //true
    }
}

class Singleton {
    private static volatile Singleton instance;
    private Singleton() {}

    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static synchronized Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

优缺点

  • 使用类装载机制,保证实例化时只有一个线程
  • 调用 getInstance 时再装载 SingletonInstance 类,完成实例化,实现延迟加载,效率高
  • 类的静态属性,只能再第一次加载类的时候初始化,JVM保证了线程安全性

推荐使用

枚举

方法

public class Single8 {
    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;

        System.out.println(instance == instance2); //true

        instance.sayOK();
    }
}

enum Singleton {
    INSTANCE;
    public void sayOK() {
        System.out.println("ok");
    }
}

优缺点

  • 借助枚举类实现单例模式,避免多线程同步问题
  • 还能防止反序列化重新创建新的对象
  • Josh Bloch 提倡使用的方式

推荐使用

分析

JDK中的Runtime

public class Runtime {
    private static final Runtime currentRuntime = new Runtime();

    private static Version version;

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class {@code Runtime} are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the {@code Runtime} object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}

    /**
     * Terminates the currently running Java virtual machine by initiating its
     * shutdown sequence.  This method never returns normally.  The argument
     * serves as a status code; by convention, a nonzero status code indicates
     * abnormal termination.
     *
     * <p> All registered {@linkplain #addShutdownHook shutdown hooks}, if any,
     * are started in some unspecified order and allowed to run concurrently
     * until they finish.  Once this is done the virtual machine
     * {@linkplain #halt halts}.
     *
     * <p> If this method is invoked after all shutdown hooks have already
     * been run and the status is nonzero then this method halts the
     * virtual machine with the given status code. Otherwise, this method
     * blocks indefinitely.
     *
     * <p> The {@link System#exit(int) System.exit} method is the
     * conventional and convenient means of invoking this method.
     *
     * @param  status
     *         Termination status.  By convention, a nonzero status code
     *         indicates abnormal termination.
     *
     * @throws SecurityException
     *         If a security manager is present and its
     *         {@link SecurityManager#checkExit checkExit} method does not permit
     *         exiting with the specified status
     *
     * @see java.lang.SecurityException
     * @see java.lang.SecurityManager#checkExit(int)
     * @see #addShutdownHook
     * @see #removeShutdownHook
     * @see #halt(int)
     */
    public void exit(int status) {
        @SuppressWarnings("removal")
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkExit(status);
        }
        Shutdown.exit(status);
    }

这是一个懒汉式,线程安全的写法。

注意事项

  • 保证类只存在一个对象,节省系统资源,对于一些需要频繁销毁的对象,单例模式可以提高系统性能
  • 如果实例化单例类时,由于构造器私有,要使用对应的方法,不能使用new

使用场景

  • 频繁进行创建、销毁的对象
  • 创建时耗费资源多(重量级对象),但又经常用到的对象
  • 工具类对象
  • 频繁访问的数据库或文件的对象

可以使用的方法

  • 饿汉式(静态常量)
  • 饿汉式(静态代码块)
  • 双重检查
  • 静态内部类
  • 枚举

工厂模式

简单工厂模式

需求

对于一个披萨项目:要便于披萨种类的扩展,便于维护

  1. 披萨的种类很多
  2. 披萨的制作流程固定有:prepare\bake\cut\box
  3. 完成披萨店订购功能

原始思路:

  • 创建一个抽象类,抽象类中含有上述 4 个方法
  • 对于其它披萨,继承抽象类,并实现自己的方法
  • 创建一个/多个order类调用各类披萨
  • 创建一个/多个store类使用对应的order类

总结:

  • 比较好理解,简单
  • 违反了OCP原则,即对扩展开放,对修改关闭

以加入新增一个pizza的类为例:

  1. 新增一个类继承pizza
  2. 需要修改每一个order类,加入功能

分析和改进思路:

  • 修改代码可以接受,但有多个order类用到创建新pizza的代码,每个order类都需要修改
  • 把创建Pizza对象封装到一个类中,这样只需要修改该类即可

基本介绍

  • 简单工厂模式属于创建型模式,是工厂模式的一种
  • 由一个工厂对象,决定创建哪一种产品的实例
  • 定义了一个创建对象的类,这个类封装实例化对象的代码
  • 当用到大量的创建某种、某类或者某批对象时,就会用到工厂模式

工厂方法模式

需求

客户在点披萨时,可以点不同口味的披萨,也可点不同地域风味的披萨,如:北京的奶酪pizza,伦敦的胡椒pizza

  • 使用简单工厂模式,创建不同的简单工厂类,如 BJPizzaSimpleFactory 和 LDPizzaSimpleFactory 等
  • 使用工厂模式

简单工厂模式对于当前这个案例来说是可行的,但考虑项目的规模时,可维护性、扩展性不是很好

基本介绍

工厂设计模式方案:将披萨项目由实例化功能抽象成抽象方法,在不同地域风味点餐子类中具体实现

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

抽象工厂模式

方法

  • 创建了一个接口,用于创建相关或有依赖关系的对象簇,而无需指名具体的类
  • 抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合
  • 抽象工厂模式时对简单工厂模式的改进
  • 将工厂抽象成了两层,AbsFactory(抽象工厂)和具体实现工厂子类

分析

JDK中的Calender

private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                cal = switch (caltype) {
                    case "buddhist" -> new BuddhistCalendar(zone, aLocale);
                    case "japanese" -> new JapaneseImperialCalendar(zone, aLocale);
                    case "gregory"  -> new GregorianCalendar(zone, aLocale);
                    default         -> null;
                };
            }
        }
        if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }

使用了一个简单工厂模式,根据不同的输入(地域、日历类型)返回不同的日历

意义

  • 将实例化的代码提取出来,放到一个类中同一进行维护,和主项目的依赖关系解耦,提高扩展、维护性
  • 共有三种工厂模式
  • 遵循设计模式的依赖抽象原则

依赖抽象原则

  • 创建对象时,不要直接使用new,而把new放在工厂的方法中,并返回 。
  • 不要让类继承具体类,而是继承抽象类或者实现接口
  • 不要覆盖基类中已经实现了的方法

原型模式

克隆羊

需求

现在有一只羊Tom

  • 姓名:Tom
  • 年龄:1
  • 颜色:白色

现在创建和tom属性完全相同的10只羊

原始思路:

定义一个 Sheep 类:

public class Sheep {
    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;
    }
    // Getter and Setter
    //......
    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }
}

使用时:

public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("tom",1,"白色");
        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        // ......
        System.out.println(sheep);
        System.out.println(sheep2);
        // ......
    }
}

分析和改进

  • 好理解,简单
  • 创建新对象时,总是需要获取原始对象的属性,创建对象比较复杂时,效率低
  • 总是需要重新初始化对象

注:Java 中 Object 类是所有类的根类,Object 类提供了一个 clone 方法,该方法可以将 Java 对象复制一份,但需要实现 clone 的 Java 类需要先实现接口 Cloneable,表示该类能够复制,且具有复制的能力。

基本介绍

  • 原型模式(Prototype)是指使用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
  • 原型设计模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
  • 工作原理是通过将一个原型对象传给要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝他们自己来实现创建

在 Sheep 类中实现 Cloneable 接口

    protected Object clone() {
        Sheep sheep = null;
        try {
            sheep = (Sheep) super.clone();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return sheep;
    }

使用时

public class Client {
    public static void main(String[] args) {
        System.out.println("Proto Type Clone");
        Sheep sheep = new Sheep("tom",1,"white");
        Sheep sheep1 = (Sheep) sheep.clone();
        Sheep sheep2 = (Sheep) sheep.clone();
        Sheep sheep3 = (Sheep) sheep.clone();
        Sheep sheep4 = (Sheep) sheep.clone();
        Sheep sheep5 = (Sheep) sheep.clone();

        System.out.println("sheep1 ="+sheep1);
        System.out.println("sheep2 ="+sheep2);
        System.out.println("sheep3 ="+sheep3);
        System.out.println("sheep4 ="+sheep4);
        System.out.println("sheep5 ="+sheep5);
    }
}

当羊类需要添加默认属性时,如地域属性,只需要再 Sheep 类中添加对应的属性和默认值

浅拷贝和深拷贝

在上述问题中,如果 Sheep 类中,包含一个对象,在克隆时,只是将一个引用指向这个对象,这是一个浅拷贝。

省略 Get、Set 和 toString 方法的 Sheep 类:

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

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

使用时:

public class Client {
    public static void main(String[] args) {
        System.out.println("Proto Type Clone");
        Sheep sheep = new Sheep("tom",1,"White");
        sheep.friend = new Sheep("jack",2,"black");
        Sheep sheep1 = (Sheep) sheep.clone();
        Sheep sheep2 = (Sheep) sheep.clone();
        Sheep sheep3 = (Sheep) sheep.clone();
        Sheep sheep4 = (Sheep) sheep.clone();
        Sheep sheep5 = (Sheep) sheep.clone();

        System.out.println("sheep1 ="+sheep1+"sheep2.friend = "+sheep2.hashCode());
        System.out.println("sheep2 ="+sheep2+"sheep2.friend = "+sheep2.hashCode());
        System.out.println("sheep3 ="+sheep3+"sheep2.friend = "+sheep2.hashCode());
        System.out.println("sheep4 ="+sheep4+"sheep2.friend = "+sheep2.hashCode());
        System.out.println("sheep5 ="+sheep5+"sheep2.friend = "+sheep2.hashCode());
    }
}

得到结果:

sheep1 =Sheep{name='tom', age=1, color='White'}sheep2.friend = 381259350
sheep2 =Sheep{name='tom', age=1, color='White'}sheep2.friend = 381259350
sheep3 =Sheep{name='tom', age=1, color='White'}sheep2.friend = 381259350
sheep4 =Sheep{name='tom', age=1, color='White'}sheep2.friend = 381259350
sheep5 =Sheep{name='tom', age=1, color='White'}sheep2.friend = 381259350

hashcode全部相同

浅拷贝:

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新对象
  • 对于数据类型是引用数据类型的成员变量,比如成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象,实际上原来的对象和克隆出的对象都指向同一个实例

深拷贝:

  • 复制对象的所有基本数据类型的成员变量值
  • 为所有引用数据类型的成员变量申请内存空间,并复制每个引用数据类型成员变量所引用的对象,知道该对象可达的所有对象,也就是说,随想深拷贝要对整个对象进行拷贝

深拷贝的实现方法:

  1. 重写clone方法来实现
  2. 对象序列化来实现

有一个类DeepCloneAble实现了 Serializable 和 Cloneable 接口

import java.io.Serializable;
import java.util.Objects;

public class DeepCloneAble implements Serializable, Cloneable {
    public static final long serialVersionUID = 1L;
    private String cloneName;
    private String cloneClass;

    public DeepCloneAble(String cloneName, String cloneClass) {
        this.cloneName = cloneName;
        this.cloneClass = cloneClass;
    }
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

使用两种不同的方式clone

import java.io.*;
import java.util.Objects;

public class DeepProtoType implements Serializable, Cloneable{
    public String name;
    public DeepCloneAble deepCloneAble;

    public DeepProtoType() {
    }

    //type1 override clone
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deep = null;
        deep = super.clone();
        DeepProtoType deepProtoType = (DeepProtoType) deep;
        deepProtoType.deepCloneAble = (DeepCloneAble) deepCloneAble.clone();
        return deep;
    }

    //type2 Serialize
    public Object deepClone() {
        ByteArrayOutputStream bos = null;
        ObjectOutput 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);
            DeepProtoType copyObj = (DeepProtoType) ois.readObject();
            return copyObj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e2) {
                System.out.println(e2.getMessage());
            }
        }
    }
}

测试

public class Client {
    public static void main(String[] args) throws Exception {
        DeepProtoType p = new DeepProtoType();
        p.name = "a";
        p.deepCloneAble = new DeepCloneAble("b", "bcalss");

        DeepProtoType p2 = (DeepProtoType) p.clone();
        System.out.println("p1.name= " + p.name + "p1,deepCloneAble.hashcode=" + p.deepCloneAble.hashCode());
        System.out.println("p2.name= " + p2.name + "p2,deepCloneAble.hashcode=" + p2.deepCloneAble.hashCode());

        DeepProtoType p20 = (DeepProtoType)p.deepClone();
        System.out.println("p20.name= " + p2.name + "p20,deepCloneAble.hashcode=" + p20.deepCloneAble.hashCode());
    }
}

建造者模式

需求

盖房子,步骤为:

  1. 地基
  2. 砌墙
  3. 盖房顶

有三种不同房子:普通、别墅、大厦,按照原始的思路设计:

建立一个抽象类,包含三个步骤

public abstract class AbstractHouse {
    public abstract void buildBasic();
    public abstract void buildWalls();
    public abstract void roofed();

    public void build() {
        buildBasic();
        buildWalls();
        roofed();
    }
}

以普通房子为例,继承这个抽象类

public abstract class AbstractHouse {
    public abstract void buildBasic();
    public abstract void buildWalls();
    public abstract void roofed();

    public void build() {
        buildBasic();
        buildWalls();
        roofed();
    }
}

使用

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

分析和改进:

  • 简单,易操作
  • 没有缓存层对象,扩展和维护性不好,把产品(房子)和创建产品的过程(盖房子)封装在了一起,耦合性强

基本介绍

  • 建造者模式是一种对象构建模式,可以将负载的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同属性的对象
  • 一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类别和内容就可以构建他们,不需要知道内部的具体构建细节

四个核心角色:

  • Porduct 产品角色:一个具体的产品对象
  • Builder 抽象建造者:创建一个Product对象的纷纷部件指定的接口/抽象类
  • ConcreateBuilder 具体建造者:实现接口,构建和装配各个部件
  • Director 指挥者:构建一个使用Builder接口的对象,主要用于创建一个复杂的对象,隔离客户与对象的生产过程、负责控制产品对象的生产过程

Product

public class House {
    private String base;
    private String wall;
    private String roofed;

    public String getBase() {
        return base;
    }

    public void setBase(String base) {
        this.base = base;
    }

    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;
    }
}

Builder

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;
    }
}

Director

public class HouseDirector {
    HouseBuilder houseBuilder = null;

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

    public void setHouseBuilder(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    public House construct() {
        houseBuilder.buildBasic();
        houseBuilder.buildWall();
        houseBuilder.roofed();
        return houseBuilder.buildHouse();
    }
}

本例中 ConcreateBuilder 有两个分别实现 common 和 high

public class CommonBuilding extends HouseBuilder{
    @Override
    public void buildBasic() {
        System.out.println("common basic");
    }

    @Override
    public void buildWall() {
        System.out.println("common wall");
    }

    @Override
    public void roofed() {
        System.out.println("common roofed");
    }
}
public class HighBuilding extends HouseBuilder{
    @Override
    public void buildBasic() {
        System.out.println("high basic");
    }

    @Override
    public void buildWall() {
        System.out.println("high wall");
    }

    @Override
    public void roofed() {
        System.out.println("high roofed");
    }
}

使用

public class Client {
    public static void main(String[] args) {
        HighBuilding highBuilding = new HighBuilding();
        HouseDirector houseDirector = new HouseDirector(highBuilding);
        houseDirector.construct();

        CommonBuilding commonBuilding = new CommonBuilding();
        houseDirector.setHouseBuilder(commonBuilding);
        houseDirector.construct();
    }
}

适配器模式

概念

基本介绍

  • 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。别名包装器(Wrapper)
  • 适配器模式属于结构型模式
  • 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式

工作原理

  • 适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
  • 从用户的角度看不到被适配者,是解耦的
  • 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
  • 用户收到反馈结果,感觉只是和目标接口交互

类适配器

以手机充电器为例:

  • 有一个类提供了一个 output 方法,返回一个 src 值为 220
  • 一个手机类,需要 5V 电压,得到 5V 电压时打印 success
  • 一个 5V 接口
  • 一个 Adapter 得到 output 并实现接口,且满足手机的需要

Vlotage220V

public class Voltage220V {
    public int output220V() {
        int src = 220;
        System.out.println("V=" + src);
        return src;
    }
}

Interface Voltage5V

public interface Voltage5V {
    public int output5V();
}

Phone

public class Phone {
    public void charging (Voltage5V voltage5V) {
        if (voltage5V.output5V() == 5) {
            System.out.println("success");
        } else {
            System.out.println("failed");
        }
    }
}

Adapter

public class VoltageAdapter extends Voltage220V implements Voltage5V{
    @Override
    public int output5V() {
        int src = output220V();
        int dstV = src/44;
        return dstV;
    }
}

使用

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

得到输出 success

通过 Adapter, Phone得到了5V电压,且不知道使用了220V的电压

  • Java 是单继承机制,所以类适配器需要继承 src 类这一点算是一个缺点, 因为这要求 dst 必须是接口,有一定局限性
  • src 类的方法在 Adapter 中都会暴露出来,也增加了使用的成本
  • 由于其继承了 src 类,所以它可以根据需求重写 src 类的方法,使得 Adapter 的灵活性增强了

对象适配器

  • 基本思路和类的适配器模式相同,只是将 Adapter 类作修改,不是继承 src 类,而是持有 src 类的实例,以解决兼容性的问题。 即:持有 src 类,实现 dst 类接口
  • 根据“合成复用原则”,在系统中尽量使用关联关系(聚合)来替代继承关系
  • 对象适配器模式是适配器模式常用的一种
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇
隐藏
变装