5 minute read

结构型模式

关注类与对象的组合。将不同的类结合在一起形成结构。

1. 适配器模式(Adapter)

类似于现在的手机充电器。电源是220V的,手机需要5V的,通过适配器进行转化,让手机充上电。以及三脚插座与两脚插座的转换器。

代码实例

//家用电源提供220V电压
public class HomeBattery {
    int supply() {
        return 220;
    }
}
//USB数据线只支持5V电压
public class UsbLine {
    void charge(int volt) {
        if (volt != 5) {
            throw new IllegalArgumentException("只能接受5V电压");
        }
        System.out.println("正常充电");
    }
}
//适配器,让家用电压降低至5V再充电
public class Adapter {
    int convert(int homeVolt) {
        int chargeVolt = homeVolt - 215;
        return chargeVolt;
    }
}
public class User {
    public static void main(String[] args) {
        HomeBattery battery = new HomeBattery();
        int homevolt = battery.supply();
        System.out.println("电压是:" + homevolt + "V");
        //直接充电是充不了的,使用适配器之后就可以充电了
        Adapter adapter = new Adapter();
        int chargeVolt = adapter.convert(homevolt);
        UsbLine usbLine = new UsbLine();
        usbLine.charge(chargeVolt);
    }
}

2. 桥接模式(Bridge)

将抽象部分与它的实现部分分离,使它们都可以独立地变化

代码实例

//形状接口
public interface Shape {
    void draw();
}
public class Rectangle implements Shape {
    private Color color;

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

    @Override
    public void draw() {
        System.out.println("画" + color.getColor() + "矩形");
    }
}
public class Round implements Shape {
    private Color color;

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

    @Override
    public void draw() {
        System.out.println("画" + color.getColor() + "圆形");
    }
}
//颜色接口
public interface Color {
    String getColor();
}
public class Blue implements Color{
    @Override
    public String getColor() {
        return "蓝色";
    }
}
public class Yellow implements Color{
    @Override
    public String getColor() {
        return "黄色";
    }
}

测试

public class Demo {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.setColor(new Blue());
        rectangle.draw();

        Round round = new Round();
        round.setColor(new Yellow());
        round.draw();
    }
}

结果

画蓝色矩形
画黄色圆形

可以发现,如果不适用桥接模式,那么每个图形都要实现2个颜色,就会有4个类(2个图像和2种颜色),导致管理复杂。桥接模式适用于两个或多个同等级的接口(本例是图形的颜色和形状)

3. 组合模式(Composite)

把一组相似的对象当做一个单一的对象

代码实例

如现在有两个类,管理员和职工

public class Manager {
    // 职位
    private String position;
    // 工作内容
    private String job;
    // 管理员的管理者
    private List<Manager> managers = new ArrayList<>();
    // 管理的职员
    private List<Employee> employees = new ArrayList<>();

    public Manager(String position, String job) {
        this.position = position;
        this.job = job;
    }
    
    public void addManager(Manager manager) {
        managers.add(manager);
    }

    public void removeManager(Manager manager) {
        managers.remove(manager);
    }

    public void addEmployee(Employee employee) {
        employees.add(employee);
    }

    public void removeEmployee(Employee employee) {
        employees.remove(employee);
    }

    // 做自己的本职工作
    public void work() {
        System.out.println("我是" + position + ",我正在" + job);
    }
    
    // 检查下属
    public void check() {
        work();
        for (Employee employee : employees) {
            employee.work();
        }
        for (Manager manager : managers) {
            manager.check();
        }
    }
}
public class Employee {
    // 职位
    private String position;
    // 工作内容
    private String job;

    public Employee(String position, String job) {
        this.position = position;
        this.job = job;
    }

    // 做自己的本职工作
    public void work() {
        System.out.println("我是" + position + ",我正在" + job);
    }
}

会发现这两个类很多部分都是重复的,那么可以用一个抽象类提取重复部分,让这两个类都继承抽象类。修改后的代码如下

public abstract class Component {
    // 职位
    private String position;
    // 工作内容
    private String job;

    public Component(String position, String job) {
        this.position = position;
        this.job = job;
    }

    // 做自己的本职工作
    public void work() {
        System.out.println("我是" + position + ",我正在" + job);
    }

    abstract void addComponent(Component component);

    abstract void removeComponent(Component component);

    abstract void check();
}
public class Manager extends Component {
    List<Component> list = new ArrayList<>();

    public Manager(String position, String job) {
        super(position, job);
    }

    @Override
    void addComponent(Component component) {
        list.add(component);
    }

    @Override
    void removeComponent(Component component) {
        list.remove(component);
    }

    @Override
    void check() {
        work();
        for (Component component : list) {
            component.check();
        }
    }
}
public class Employee extends Component {

    public Employee(String position, String job) {
        super(position, job);
    }

    @Override
    void addComponent(Component component) {
        System.out.println("无权限");
    }

    @Override
    void removeComponent(Component component) {
        System.out.println("无权限");
    }

    @Override
    void check() {
        work();
    }
}

在调用的时候只需要调用抽象类即可。

public class Demo {
    public static void main(String[] args) {
        Component boss = new Manager("老板", "唱怒放的生命");
        Component HR = new Employee("人力资源", "聊微信");
        Component PM = new Manager("产品经理", "不知道干啥");
        Component CFO = new Manager("财务主管", "看剧");
        Component CTO = new Manager("技术主管", "划水");
        Component UI = new Employee("设计师", "画画");
        Component operator = new Employee("运营人员", "兼职客服");
        Component webProgrammer = new Employee("程序员", "学习设计模式");
        Component backgroundProgrammer = new Employee("后台程序员", "CRUD");
        Component accountant = new Employee("会计", "背九九乘法表");
        Component clerk = new Employee("文员", "给老板递麦克风");
        boss.addComponent(HR);
        boss.addComponent(PM);
        boss.addComponent(CFO);
        PM.addComponent(UI);
        PM.addComponent(CTO);
        PM.addComponent(operator);
        CTO.addComponent(webProgrammer);
        CTO.addComponent(backgroundProgrammer);
        CFO.addComponent(accountant);
        CFO.addComponent(clerk);

        boss.check();
    }
}

4. 装饰器模式(Decorator)

不改变原有类的基础上增强类的原有功能或者为一个类添加新功能

增强原有功能

public interface Money {
    int getMoney();
}
public class AliPay implements Money {
    @Override
    public int getMoney() {
        return 100;
    }
}
public class Stock implements Money {
    public final Money aliPay;

    public Stock(Money aliPay) {
        this.aliPay = aliPay;
    }

    @Override
    public int getMoney() {
        return aliPay.getMoney() + 20;
    }
}
public class Demo {
    public static void main(String[] args) {
        Money money = new AliPay();
        System.out.println(money.getMoney());
	    //money经过stock增强之后变多了
        Money moneyWithStock = new Stock(money);
        System.out.println(moneyWithStock.getMoney());
    }
}

装饰器没有改变接口,也没有新增方法,所以被称之为透明装饰模式,特点是可以无限装饰

添加功能

//新增装饰器接口,因为是对原有的接口的扩展,所以需要继承原有类
public interface Bank extends Money {
    void loan();
}
//实现接口
public class WeChat implements Bank {
    private final Money money;

    public WeChat(Money money) {
        this.money = money;
    }

    @Override
    public void loan() {
        System.out.println("借钱");
    }

    @Override
    public int getMoney() {
        return money.getMoney()+10;
    }
}
public class Demo {
    public static void main(String[] args) {
        Money money = new AliPay();
        System.out.println(money.getMoney());
        
        Bank moneyCanLoad = new WeChat(money);
        System.out.println(moneyCanLoad.getMoney());
        moneyCanLoad.loan();
    }
}

这样就达到了新增功能的目的。客户端要使用新方法,必须知道装饰类,但是被装饰者是对客户端不可见的,所以被称之为半透明装饰模式

Java I/O 设计框架用的就是装饰器模式,在基础的InputStream和OutputStream上做功能的增强。

装饰器模式与适配器模式的区别

这两者很容易混淆

  • 适配器模式仅用于改变接口
  • 装饰器模式不改变原有接口,只是增强或者添加功能。

    5. 外观模式(Facade)

    将子系统封装起来,提供一个简洁的接口供外界调用。MVC架构就是这种设计模式的体现,Controller供其他人调用。因为太过于简单,不举代码实例了。

    6. 享元模式(Flyweight)

    使用共享支持大量细粒度对象。 使用场景:需要使用大量的对象。 魔法药店里有大量的药 ```java public interface Potion { void drink(); }

public class HealingPotion implements Potion { @Override public void drink() { // drink } }

public class HolyWaterPotion implements Potion{ @Override public void drink() { //drink
} }

public class InvisibilityPotion implements Potion{ @Override public void drink() { // } }


```java
public class PotionFactory {
    private final Map<PotionType, Potion> potions;

    public PotionFactory() {
        potions = new EnumMap<>(PotionType.class);
    }

    Potion createPotion(PotionType type) {
        var potion = potions.get(type);
        if (potion == null) {
            switch (type) {
                case HEALING -> {
                    potion = new HealingPotion();
                    potions.put(type, potion);
                }
                case HOLY_WATER -> {
                    potion = new HolyWaterPotion();
                    potions.put(type, potion);
                }
                case INVISIBILITY -> {
                    potion = new InvisibilityPotion();
                    potions.put(type, potion);
                }
                default -> {
                }
            }
        }
        return potion;
    }
}
public class AlchemistShop {
    private final List<Potion> topShelf;
    private final List<Potion> bottomShelf;

    public AlchemistShop() {
        var factory=new PotionFactory();
        topShelf=List.of(
                factory.createPotion(PotionType.INVISIBILITY),
                factory.createPotion(PotionType.INVISIBILITY),
                factory.createPotion(PotionType.STRENGTH),
                factory.createPotion(PotionType.HEALING),
                factory.createPotion(PotionType.INVISIBILITY),
                factory.createPotion(PotionType.STRENGTH),
                factory.createPotion(PotionType.HEALING),
                factory.createPotion(PotionType.HEALING)
        );
        bottomShelf =List.of(
                factory.createPotion(PotionType.POISON),
                factory.createPotion(PotionType.POISON),
                factory.createPotion(PotionType.POISON),
                factory.createPotion(PotionType.HOLY_WATER),
                factory.createPotion(PotionType.HOLY_WATER)
        );
    }

    public final List<Potion> getTopShelf() {
        return List.copyOf(this.topShelf);
    }

    public final List<Potion> getBottomShelf() {
        return List.copyOf(this.bottomShelf);
    }

    public void drinkPotions() {
        LOGGER.info("Drinking top shelf potions\n");
        topShelf.forEach(Potion::drink);
        LOGGER.info("Drinking bottom shelf potions\n");
        bottomShelf.forEach(Potion::drink);
    }
}

7. 代理模式(Proxy)

给某个对象提供代理,由代理控制对原对象的引用。(有些时候直接访问会比较麻烦,那么可以访问该对象的代理对象来操作)

静态代理

public interface Http {
    void request(String sendData);

    void onSuccess(String receivedData);
}
public class HttpUtil implements Http {
    @Override
    public void request(String sendData) {
        System.out.println("网络请求中...");
    }

    @Override
    public void onSuccess(String receivedData) {
        System.out.println("网络请求完成。");
    }
}
//代理类
public class HttpProxy implements Http {

    private final HttpUtil httpUtil;

    public HttpProxy(HttpUtil httpUtil) {
        this.httpUtil = httpUtil;
    }

    @Override
    public void request(String sendData) {
        System.out.println("发送" + sendData);
        httpUtil.request(sendData);
    }

    @Override
    public void onSuccess(String receivedData) {
        System.out.println("收到" + receivedData);
        httpUtil.onSuccess(receivedData);
    }
}
public class Demo {
    public static void main(String[] args) {
        HttpProxy httpProxy = new HttpProxy(new HttpUtil());
        httpProxy.request("消息");
        httpProxy.onSuccess("测试");
    }
}

结果

发送消息
网络请求中...
收到测试
网络请求完成

动态代理

和静态代理的区别是,如果代理对象有很多方法,那么静态代理需要全部实现这些方法,而动态代理可以选择性的实现部分。

//动态代理的实现比较麻烦,需要使用反射
public class HttpProxy implements InvocationHandler {
    private HttpUtil httpUtil;

    public Http getInstance(HttpUtil httpUtil) {
        this.httpUtil = httpUtil;
        //专门用于动态代理的方法
        return (Http) Proxy.newProxyInstance(httpUtil.getClass().getClassLoader(), httpUtil.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
        Object result = null;
        if (method.getName().equals("request")) {
            System.out.println("发送数据" + args[0]);
            result = method.invoke(httpUtil, args);
        } else if (method.getName().equals("onSuccess")) {
            System.out.println("收到数据" + args[0]);
            result = method.invoke(httpUtil, args);
        }
        return result;
    }
}