结构型模式

1. 适配器模式*

适配器模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

SpringMVC中的HandlerAdapter就使用了适配器模式

1.1 类适配器模式

// 客户希望使用的接口:destination
interface MyStack {
    void pop();
}

// 被适配者:source
class MyList {
    public void removeFirst() {
        System.out.println("名叫removeFirst的出栈");
    }
}

// 类适配器:通过继承Source类、实现Destination接口,完成Source->Destination的适配
class Adapter extends MyList implements MyStack {
    @Override
    public void pop() {
        this.removeFirst();
    }
}

public class AdapterClient {
    public static void main(String[] args) {
        MyStack stack = new Adapter();
        stack.pop();
    }
}

1.2 对象适配器模式

// 客户希望使用的接口:destination
interface MyStack {
    void pop();
}

// 被适配者:source
class MyList {
    public void removeFirst() {
        System.out.println("名叫removeFirst的出栈");
    }
}

/**
 * 对象适配器*:通过复合,除了满足“用户期待接口”,还降低了代码间的不良耦合
 * 通过复合Source类、实现Destination接口,完成Source->Destination的适配
 */
class Adapter implements MyStack {
    private MyList myList = new MyList();

    @Override
    public void pop() {
        myList.removeFirst();
    }
}

public class AdapterClient {
    public static void main(String[] args) {
        MyStack stack = new Adapter();
        stack.pop();
    }
}

1.3 缺省适配器模式

// 客户希望使用的接口:destination
interface ListStack {
    void pop();

    void removeFirst();
}

/**
 * 缺省适配器:当不需要使用一个接口的全部方法时,
 * 可以设计一个抽象类实现接口的所有方法,但有的方法可以做成空方法,
 * 那么该抽象类的子类可以选择性的重写父类的某些方法
 */
abstract class Adapter implements ListStack {
    @Override
    public void pop() {
    }

    @Override
    public void removeFirst() {
    }
}

// 被适配者:source
class MyList extends Adapter {
    @Override
    public void removeFirst() {
        System.out.println("我只需要removeFirst()方法");
    }
}

public class AdapterClient {
    public static void main(String[] args) {
        ListStack list = new MyList();
        list.removeFirst();
        // Source类也可以用匿名内部类来代替
        ListStack stack = new Adapter() {
            @Override
            public void pop() {
                System.out.println("我只需要pop()方法");
            }
        };
        stack.pop();
    }
}

2. 桥接模式

桥接模式:将抽象部分与它的实现部分分离,使它们可以独立地变化,又称为柄体模式或接口模式

桥接模式模式基于单一职责原则,通过使用封装、聚合及继承等方式让不同的类承担不同的职责

桥梁模式所涉及的角色:

  1. 实现化角色(Implementor):这个角色给出实现化角色的接口
  2. 具体实现化角色(Concrete Implementor):这个角色给出实现化角色接口的具体实现
  3. 抽象化角色(Abstraction):抽象化给出的定义,并保存一个对实现化对象的引用
  4. 修正抽象化角色(Refined Abstraction):抽象化角色的子类,改变和修正父类对抽象化的定义

实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作

// 实现化角色:颜色
interface Color {
    void showColor();
}

// 具体实现化角色:黑色
class Black implements Color {
    @Override
    public void showColor() {
        System.out.print("黑色的");
    }
}

// 具体实现化角色:白色
class White implements Color {
    @Override
    public void showColor() {
        System.out.print("白色的");
    }
}

// 抽象化角色:在内部聚合一个实现化角色
abstract class Animal {
    Color color;

    public void show() {
        this.color.showColor();
    }
}

// 修正抽象化角色:狗
class Dog extends Animal {
    @Override
    public void show() {
        super.show();
        System.out.println("狗汪汪叫");
    }
}

// 修正抽象化角色:猫
class Cat extends Animal {
    @Override
    public void show() {
        super.show();
        System.out.println("猫喵喵叫");
    }
}

public class BridgeClient {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.color = new Black();
        // 黑色的狗汪汪叫
        dog.show();

        Cat cat = new Cat();
        cat.color = new White();
        // 白色的猫喵喵叫
        cat.show();
    }
}

3. 装饰模式*

装饰模式:在不必改变原类文件和使用继承的情况下,动态地给一个对象添加一些额外的职责;它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

装饰模式特点:

  1. 装饰对象和真实对象有相同的接口
  2. 装饰对象包含一个真实对象的引用
  3. 装饰对象接受所有来自客户端的请求,装饰对象可以在转发这些请求以前或以后增加一些附加功能

装饰模式所涉及的角色:

  1. 抽象构件角色(Component):给出一个抽象接口,以规范准备接收附加责任的对象
  2. 具体构件角色(Concrete Component):定义一个将要接收附加责任的类
  3. 装饰角色(Decorator):持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口
  4. 具体装饰角色(Concrete Decorator):负责给构件对象添加上附加的责任

装饰模式简化:

  1. 如果只有一个具体构件角色而没有抽象构件角色时,可以让装饰角色继承具体构件角色
  2. 如果只有一个具体装饰角色时,可以将装饰角色和具体装饰角色合并

装饰模式适用情况:

  1. 需要扩展一个类的功能,或给一个类添加附加职责
  2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销
  3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能
  4. 当不能采用生成子类的方法进行扩充时

Java的IO流就采用了装饰模式

装饰者模式原理类图:

// 抽象构件角色
interface IO {
    void copy();
}

// 具体构件角色
class MyIO implements IO {
    @Override
    public void copy() {
        System.out.println("拷贝");
    }
}

// 装饰角色
class Decorator implements IO {
    IO io;

    public Decorator(IO io) {
        this.io = io;
    }

    @Override
    public void copy() {
        io.copy();
    }
}

// 具体装饰角色:BufferedIO
class BufferedIO extends Decorator {
    public BufferedIO(IO io) {
        super(io);
    }

    @Override
    public void copy() {
        super.copy();
        addBuffered();
    }

    public void addBuffered() {
        System.out.println("拥有缓冲功能");
    }
}

// 具体装饰角色:LineNumberIO
class LineNumberIO extends Decorator {
    public LineNumberIO(IO io) {
        super(io);
    }

    @Override
    public void copy() {
        super.copy();
        addLineNumber();
    }

    public void addLineNumber() {
        System.out.println("拥有操作行号功能");
    }
}

public class DecoratorClient {
    public static void main(String[] args) {
        IO io = new Decorator(new MyIO());
        io = new BufferedIO(io);
        io = new LineNumberIO(io);
        // 调用过程类似递归,一层一层装饰
        io.copy();
        /**结果:
         * 拷贝
         * 拥有缓冲功能
         * 拥有操作行号功能
         */
    }
}

4. 组合模式

组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。

组合模式所涉及的角色:

  1. Component:是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件
  2. Leaf:在组合中表示叶子结点对象
  3. Composite:定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等

java.util.HashMap就使用了组合模式

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

abstract class Component {
    String name;

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

    void add(Component component) {
        throw new UnsupportedOperationException();
    }

    void remove(Component component) {
        throw new UnsupportedOperationException();
    }

    abstract void display(int depth);
}

class Composite extends Component {
    List<Component> componentList = new ArrayList<>();

    public Composite(String name) {
        super(name);
    }

    @Override
    public void add(Component component) {
        componentList.add(component);
    }

    @Override
    public void remove(Component component) {
        componentList.remove(component);
    }

    @Override
    public void display(int depth) {
        StringBuilder sb = new StringBuilder("");
        for (int i = 0; i < depth; i++) {
            sb.append("-");
        }
        System.out.println(new String(sb) + this.name);
        for (Component component : componentList) {
            component.display(depth + 3);
        }
    }
}

class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }

    @Override
    public void display(int depth) {
        StringBuilder sb = new StringBuilder("");
        for (int i = 0; i < depth; i++) {
            sb.append("-");
        }
        System.out.println(new String(sb) + this.name);
    }
}

public class CompositeClient {
    public static void main(String[] args) {
        Composite root = new Composite("数计学院");
        Composite jsj = new Composite("计算机专业");
        Composite wl = new Composite("网络专业");
        jsj.add(new Leaf("设计模式"));
        jsj.add(new Leaf("数据结构"));
        wl.add(new Leaf("网络原理"));
        root.add(jsj);
        root.add(wl);
        root.display(0);
        /**结果:类似DOM树
         * 数计学院
         * ---计算机专业
         * ------设计模式
         * ------数据结构
         * ---网络专业
         * ------网络原理
         */
    }
}

5. 外观模式

外观模式(过程模式):为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得子系统更加容易使用

org.apache.ibatis.session.Configuration类的newMetaObject方法就使用了外观模式

// 子系统:CPU
class CPU {
    public void freeze() {
    }

    public void jump() {
    }

    public void execute() {
    }
}

// 子系统:Memory
class Memory {
    public void load() {
    }
}

// 外观类:为子系统提供一个共同的对外接口
class Computer {
    CPU cpu = new CPU();
    Memory memory = new Memory();

    public void startComputer() {
        cpu.freeze();
        memory.load();
        cpu.jump();
        cpu.execute();
    }
}

// 客户端:通过一个外观接口读写子系统中各接口的数据资源
public class FacadeClient {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.startComputer();
    }
}

6. 享元模式

享元模式:运用共享技术有效地支持大量细粒度的对象。使用共享物件来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件

享元模式的两个状态:

  1. 内蕴状态存储在享元内部,不会随环境的改变而有所不同,是可以共享的
  2. 外蕴状态是不可以共享的,它随环境的改变而改变的,因此外蕴状态是由客户端来保持

享元模式所涉及的角色:

  1. 抽象享元角色(FlyWeight):是具体享元的抽象,同时定义出对象的内蕴状态和外蕴状态的接口或实现
  2. 具体享元角色(ConcreteFlyWeight):实现抽象角色规定的方法。如果存在内蕴状态,就负责为内蕴状态提供存储空间
  3. 享元工厂角色(FlyWeightFactory):用于构建一个池容器,同时提供从池中获取对象方法;这个角色的实现是共享的关键
  4. 客户端角色(Client):维护对所有享元对象的引用,而且还需要存储对应的外蕴状态

享元模式能够解决重复对象的内存浪费的问题,享元模式的经典应用场景就是池技术

java.lang.Integer类的valueOf方法就采用了享元模式

import java.util.HashMap;

// 外蕴状态
class OuterStatus {
    String outerState;

    public OuterStatus(String outerState) {
        this.outerState = outerState;
    }
}

// 抽象享元角色
interface Flyweight {
    void use(OuterStatus outerStatus);
}

// 具体享元角色
class ConcreteFlyWeight implements Flyweight {
    // 共享部分,内蕴状态
    String type;

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

    @Override
    public void use(OuterStatus outerStatus) {
        System.out.println("具体享元:" + type + "\t外蕴状态:" + outerStatus.outerState);
    }
}

// 享元工厂角色
class FlyWeightFactory {
    HashMap<String, Flyweight> pool = new HashMap<>();

    public Flyweight getFlyweight(String type) {
        if (!pool.containsKey(type)) {
            pool.put(type, new ConcreteFlyWeight(type));
        }
        return pool.get(type);
    }

    public int getFlyweightSize() {
        return pool.size();
    }
}

// 客户端角色
public class FlyweightClient {
    public static void main(String[] args) {
        FlyWeightFactory flyWeightFactory = new FlyWeightFactory();
        Flyweight sqlPool1 = flyWeightFactory.getFlyweight("数据库连接池");
        sqlPool1.use(new OuterStatus("url"));
        Flyweight sqlPool2 = flyWeightFactory.getFlyweight("数据库连接池");
        sqlPool2.use(new OuterStatus("driver"));
        int flyweightSize = flyWeightFactory.getFlyweightSize();
        System.out.println(flyweightSize);
    }
}

7. 代理模式*

代理模式概念:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

代理模式组成:

  1. 抽象角色:通过接口或抽象类声明真实角色实现的业务方法
  2. 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用
  3. 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作

7.1 静态代理

静态代理是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了

真实角色和代理角色实现同一个接口

// 抽象角色
interface MyConnection {
    void createStatement();

    void close();
}

// 真实角色
class JdbcConnection implements MyConnection {
    @Override
    public void createStatement() {
        System.out.println("获取执行SQL的对象");
    }

    @Override
    public void close() {
        System.out.println("关闭连接");
    }
}

// 代理角色
class ProxyConnection implements MyConnection {
    JdbcConnection jdbcConnection = new JdbcConnection();

    @Override
    public void createStatement() {
        jdbcConnection.createStatement();
    }

    @Override
    public void close() {
        System.out.println("归还连接");
    }
}

public class ProxyClient {
    public static void main(String[] args) {
        MyConnection connection = new ProxyConnection();
        connection.createStatement();
        connection.close();
    }
}

7.2 JDK动态代理

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象

JDK动态代理是基于接口的动态代理:要求被代理类至少实现一个接口

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 抽象角色
interface MyConnection {
    void createStatement();

    void close();
}

// 真实角色
class JdbcConnection implements MyConnection {
    @Override
    public void createStatement() {
        System.out.println("获取执行SQL的对象");
    }

    @Override
    public void close() {
        System.out.println("关闭连接");
    }
}

// JDK动态代理
class JDKProxyConnectionFactory {
    MyConnection connection = new JdbcConnection();

    // 动态生成代理角色
    public MyConnection getProxyInstance() {
        return (MyConnection) Proxy.newProxyInstance(JdbcConnection.class.getClassLoader(), new Class[]{MyConnection.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object object = null;
                // 如果是close方法,就增强该方法
                if ("close".equals(method.getName())) {
                    System.out.println("归还连接");
                } else {
                    // 调用真实角色的方法
                    object = method.invoke(connection, args);
                }
                return object;
            }
        });
    }
}

public class ProxyClient {
    public static void main(String[] args) {
        JDKProxyConnectionFactory jdkProxyConnectionFactory = new JDKProxyConnectionFactory();
        MyConnection connection = jdkProxyConnectionFactory.getProxyInstance();
        connection.createStatement();
        connection.close();
    }
}

7.3 CGLib动态代理

CGLib(Code Generation Library)比java.lang.reflect.Proxy类更强的在于它不仅可以接管接口类的方法,还可以接管普通类的方法

CGLib动态代理是基于子类的动态代理:被代理类可以不必实现接口,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,因此真实角色类不能被final修饰

CGLib的底层是通过使用Java字节码操作框架ASM来转换字节码并生成新的类

使用CGLib需导入的jar包,如:aspectjweaver-1.8.7.jar(Spring的AOP依赖)或cglib-2.2.2.jar+asm-3.3.1.jar

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 真实角色
class JdbcConnection {
    public void createStatement() {
        System.out.println("获取执行SQL的对象");
    }

    public void close() {
        System.out.println("关闭连接");
    }
}

// CGLib动态代理
class CGLibProxyConnectionFactory {
    JdbcConnection jdbcConnection = new JdbcConnection();

    // 获取一个代理角色
    public JdbcConnection getProxyInstance() {
        // 1. 创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        // 2. 设置父类(要代理的类)
        enhancer.setSuperclass(jdbcConnection.getClass());
        // 3. 设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            /**
             * 参数说明:
             * Object:表示要进行增强的对象
             * Method:表示拦截的方法
             * Object[]:表示参数列表,基本数据类型需要传入其包装类型
             * MethodProxy:表示对方法的代理
             */
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                Object object = null;
                // 如果是close方法,就增强该方法
                if ("close".equals(method.getName())) {
                    System.out.println("归还连接");
                } else {
                    // 调用真实角色的方法
                    object = method.invoke(jdbcConnection, args);
                    System.out.println("---------------------");
                    // 调用真实角色的方法
                    methodProxy.invokeSuper(obj, args);
                }
                return object;
            }
        });
        // 4. 创建代理对象
        return (JdbcConnection) enhancer.create();
    }
}

public class ProxyClient {
    public static void main(String[] args) {
        CGLibProxyConnectionFactory cgLibProxyConnectionFactory = new CGLibProxyConnectionFactory();
        JdbcConnection jdbcConnection = cgLibProxyConnectionFactory.getProxyInstance();
        jdbcConnection.createStatement();
        jdbcConnection.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值