23种设计模式——结构型模型

结构型模型的核心作用是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题

适配器模式

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

模式中的角色:

  • 目标接口:客户所期待的接口。目标可以是具体或抽象的类,也可以是接口
  • 需要适配的类
  • 适配器:通过包装一个需要适配的对象,把原接口转换成目标接口

首先准备目标接口,这个接口是供客户端调用的

public interface Target {
    void handleReq();
}

然后准备被适配的类

/**
 * 被适配的类
 */
public class Adaptee {
    public void request() {
        System.out.println("可以完成客户请求的需要功能!");
    }
}

然后就是适配器

适配器适配被适配的类有两种方式:

  • 一种是直接继承被适配的类,该方式的缺点是java只能单继承,无法完成适配多个类
  • 第二种是使用对象组合的方式

方式一

/**
 * 适配器本身
 * 需要把被适配对象Adaptee跟目标对象Target连接起来
 */
public class Adapter extends Adaptee implements Target {
    @Override
    public void handleReq() {
        super.request();
    }
}

方式二

/**
 * 适配器本身
 * 需要把被适配对象Adaptee跟目标对象Target连接起来
 */
public class Adapter2 implements Target {
    private Adaptee adaptee;
    
    public Adapter2(Adaptee adaptee) {
        super();
        this.adaptee = adaptee;
    }

    @Override
    public void handleReq() {
        adaptee.request();
    }
}

使用

Target t = new Adapter();
t.handleReq();

使用场景

  • 做旧系统改造和升级

  • java.io.InputStreamReader(InputStream)

  • java.io.OutputStreamWriter(OutputStream)

代理模式

作用:通过代理,控制对对象的访问。在调用某个方法前做前置处理,调用这个方法后做后置处理

核心角色:

  • 抽象角色:定义代理角色和真实角色的公共对外方法

  • 真实角色:实现抽象角色,定义真实角色要实现的业务逻辑

  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并且可以附加自己的操作

分类:

  • 静态代理(静态定义代理类)

  • 动态代理(动态生成代理类)

静态代理

首先定义抽象角色

public interface Star {
    /**
     * 面谈
     */
    void confer();
    /**
     * 签合同
     */
    void signContract();
    /**
     * 订票
     */
    void bookTicket();
    /**
     * 唱歌
     */
    void sing();
    /**
     * 收钱
     */
    void collectMoney();
}

真实角色

public class RealStar implements Star {
    @Override
    public void bookTicket() {
        System.out.println("bookTicket");    
    }

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

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

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

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

代理角色

public class ProxyStar implements Star {
    private Star star;   // 被代理的对象

    public ProxyStar(Star star) {
        this.star = star;
    }


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

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

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

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

    @Override
    public void sing() {
        // 调用真实角色的方法
        star.sing();
    }
}

使用

Star star = new ProxyStar(new RealStar());
star.confer();
star.signContract();
star.bookTicket();
star.sing();
star.collectMoney();

动态代理

动态代理的实现方式

  • JDK自带的动态代理

  • javaassist字节码操作库实现

  • CGLIB

  • ASM(底层使用指令,可维护性较差)

使用JDK自带的动态代理

  • java.lang.reflect.Proxy

    • 该类的作用是动态的生成代理类对象

  • java.lang.reflect.InvocationHandler(处理器接口)

    • 可以通过invoke方法实现对真实角色的代理访问,所有的流程控制都在这个方法中处理

    • 每次通过Proxy生成代理类对象时都要指定对应的处理器对象

使用动态代理还是需要实现抽象角色

public interface Star {
    /**
     * 面谈
     */
    void confer();
    /**
     * 签合同
     */
    void signContract();
    /**
     * 订票
     */
    void bookTicket();
    /**
     * 唱歌
     */
    void sing();
    /**
     * 收钱
     */
    void collectMoney();
}

真实角色 这是必须的

public class RealStar implements Star {
    @Override
    public void bookTicket() {
        System.out.println("bookTicket");    
    }

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

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

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

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

代理角色

这一块换成了动态代理,需要自定义handler在执行方法前后执行某些操作

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

public class StarHandler implements InvocationHandler {
    private Star realStar;

    public StarHandler(Star star) {
        this.realStar = star;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object obj = null;
        if (method.getName().equals("sing")) {
            System.out.println("执行前");
        }
        // 执行当前方法
        obj = method.invoke(realStar, args);        
        System.out.println("执行后");
        return obj;
    }
}

使用

Star realStar = new RealStar();
StarHandler handler = new StarHandler(realStar);
Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Star.class}, handler);
proxy.bookTicket();
proxy.sing(); 

桥接模式

 

 

如果要实现上图中的结构,需要非常复杂的操作,以联想台式机为例,它需要继承台式机类,而台式机类需要实现电脑接口,过程非常的繁琐。

现在通过观察,任意一台笔记本或者是台式机,都由两部分组成,一个是品牌,一个是电脑类型,那么我们可以分别设置两个结构,分别是电脑类型跟品牌,然后将这两个结构联系起来,这中间就像搭了一座桥,所以这种模式叫做桥接模式。

首先是对品牌分类进行定义

/**
 * 品牌
 */
public interface Brand {
    void sale();
}

class Lenovo implements Brand {
    @Override
    public void sale() {
        System.out.println("联想电脑");
    }
}

class Dell implements Brand {
    @Override
    public void sale() {
        System.out.println("戴尔电脑");
    }
}

然后对电脑类型进行定义

/**
 * 电脑类型
 */
public class Computer {
    protected Brand brand;
    
    public Computer(Brand brand) {
        this.brand = brand;
    }

    public void sale() {
        brand.sale();
    }
}

class Desktop extends Computer {
    public Desktop(Brand b) {
        super(b);
    }

    @Override
    public void sale() {
        super.sale();
        System.out.println("销售台式机");
    }
}

class Laptop extends Computer {
    public Laptop(Brand b) {
        super(b);
    }

    @Override
    public void sale() {
        super.sale();
        System.out.println("销售笔记本");
    }
}

使用

现在想要一个品牌的电脑就不需要定义一个类了,直接组合即可

public static void main(String[] args) {
	Computer c = new Laptop(new Lenovo());
	c.sale();
}

组合模式

组合模式用于处理属性结构,不论是处理叶子节点还是容器节点都有一个统一的处理方式

核心组成:

  • 抽象构建角色,定义了叶子和容器构建共同遵循的规则,也是供客户端调用的接口

  • 叶子构建角色,无子节点

  • 容器构建角色,有容器特征,可以包含子节点

/**
 * 抽象组件
 */
public interface Component {
    void operation();
}

/**
 * 叶子组件
 */
interface Leaf extends Component {

}

/**
 * 容器组件
 */
interface Composite extends Component {
    void add(Component c);   // 添加组件
    void remove(Component c);  // 删除组件
    Component getChild(int index);  // 返回指定的组件
}

模拟杀毒软件架构设计

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

/**
 * 抽象构建
 */
public interface AbstractFile {
    void killVirus();   // 杀毒
}

/**
 * 这是叶子节点,因为一个图像文件里面不会再包含别的文件了
 */
class ImageFile implements AbstractFile {
    private String name;

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

    @Override
    public void killVirus() {
        System.out.println("图像文件:" + name + "进行查杀!");
    }
}

/**
 * 这是叶子节点,因为一个文本文件里面不会再包含别的文件了
 */
class TextFile implements AbstractFile {
    private String name;

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

    @Override
    public void killVirus() {
        System.out.println("文本文件:" + name + "进行查杀!");
    }
}

/**
 * 容器节点,因为一个文件夹下可以有其他的文件
 */
class Folder implements AbstractFile {
    private String name;
    // 用来存放容器下构建的子节点
    private List<AbstractFile> list = new ArrayList<>();

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

    public void add(AbstractFile file) {
        list.add(file);
    }

    public void remove(AbstractFile file) {
        list.remove(file);
    }

    public AbstractFile getChild(int index) {
        return list.get(index);
    }

    @Override
    public void killVirus() {
        System.out.println("文件夹: " + name + "进行查杀");
        list.forEach(AbstractFile::killVirus);
    }
}

使用

public static void main(String[] args) {
    Folder f1 = new Folder("我的收藏");
    AbstractFile f2, f3;
    f2 = new ImageFile("1.jpg");
    f3 = new TextFile("HHH.txt");
    f1.add(f2);
    f1.add(f3);

    f1.killVirus();
}

装饰模式

作用:

  • 动态的为一个对象增加新的功能

  • 装饰模式是一种用于替代继承的技术,无须通过继承增加子类就能扩展对象的新功能。使用对象的关联关系替代继承关系,更加灵活,同时避免类型体系的快速膨胀

实现细节

  • Component抽象构建角色:真实对象和装饰对象有相同的接口,这样客户端对象就能以与真实对象相同的方式同装饰对象交互

  • ConcreateComponent具体构建角色(真实对象)

  • Decorate装饰角色,持有一个抽象构建的引用,装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象,这样,就能在真实对象调用前后增加新的功能

  • ConcreateDecorate具体装饰角色

 

/**
 * 抽象组件
 */
public interface ICar {
    void move();
}

// 真实对象,即被装饰对象
class Car implements ICar {
    @Override
    public void move() {
        System.out.println("陆地上跑!");
    }
}

/**
 * Decorate装饰器角色
 */
class SuperCar implements ICar {
    protected ICar iCar;

    public SuperCar(ICar iCar) {
        this.iCar = iCar;
    }

    @Override
    public void move() {
        iCar.move();
    }
}

/**
 * ConcreateDecorate具体装饰角色
 */
class FlyCar extends SuperCar {

    public FlyCar(ICar iCar) {
        super(iCar);
    }

    public void fly() {
        System.out.println("天上飞");
    }

    @Override
    public void move() {
        fly();
        super.move();
    }
}

/**
 * ConcreateDecorate具体装饰角色
 */
class WaterCar extends SuperCar {

    public WaterCar(ICar iCar) {
        super(iCar);
    }

    public void swim() {
        System.out.println("水上游");
    }

    @Override
    public void move() {
        swim();
        super.move();
    }
}

使用

public static void main(String[] args) {
    Car car = new Car();
    car.move();

    System.out.println("增加新功能,飞行");
    FlyCar flyCar = new FlyCar(car);
    flyCar.move();

    System.out.println("继续增加水上跑");
    WaterCar waterCar = new WaterCar(flyCar);
    waterCar.move();
}

外观模式

迪米特法则(最少知识原则):一个软件实体应当尽可能少的与其它实体发生相互作用

核心:为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。

享元模式

作用:内存属于稀缺资源,不能随意浪费。如果有很多个完全相同或相似的对象,我们可以使用享元模式,节省内存

享元模式的关键是做到了区分内部状态跟外部状态

  • 内部状态:可以共享,不会随环境的变化而改变

  • 外部状态:不可以共享,会随环境的变化而变化

实现

  • FlyweightFactory享元工厂:创建并管理享元对象,享元池一般设计成键值对

  • Flyweight抽象享元类:通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态

  • ConcreateFlyWeight具体享元类:为内部状态提供成员变量进行存储

  • UnsharedConcreateFlyWeight非共享享元类:不能被共享的子类可以设计为非共享享元类

FlyweightFactory享元工厂

import java.util.HashMap;
import java.util.Map;

/**
 * 享元工厂类
 */
public class ChessFlyWeightFactory {
    // 享元池
    private static Map<String, ChessFlyWeight> map = new HashMap<>();

    public static ChessFlyWeight getChess(String color) {
        if (map.get(color) != null) {
            return map.get(color);
        } else {
            ChessFlyWeight cfw = new ConcreateChess(color);
            map.put(color, cfw);
            return cfw;
        }
    }
}

Flyweight抽象享元类&ConcreateFlyWeight具体享元类

/**
 * 享元类
 */
public interface ChessFlyWeight {
    void setColor(String color);
    String getColor();
    void display(Coordinate c);
}

class ConcreateChess implements ChessFlyWeight {
    private String color;

    public ConcreateChess(String color) {
        this.color = color;
    }

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

    @Override
    public String getColor() {
        return color;
    }

    @Override
    public void display(Coordinate c) {
        System.out.println("旗子颜色:" + color);
        System.out.println("棋子位置:" + c.getX() + ", " + c.getY());
    }
}

UnsharedConcreateFlyWeight非共享享元类

/**
 * 外部状态UnsharedConcreateFlyWeight
 */
public class Coordinate {
    private int x, y;

    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}

客户端调用

public static void main(String[] args) {
    ChessFlyWeight c1 = ChessFlyWeightFactory.getChess("黑色");
    ChessFlyWeight c2 = ChessFlyWeightFactory.getChess("黑色");
    System.out.println(c1);
    System.out.println(c2);

    System.out.println("增加外部状态的处理==============");
    c1.display(new Coordinate(10, 10));
    c2.display(new Coordinate(20, 20));
}

缺点

  • 模式较复杂,使程序逻辑复杂化

  • 为了节省内存,共享了内存状态,分离出外部状态,而读取外部状态,使运行时间变长。用时间换取了空间

 

posted @ 2019-10-19 11:18  Jin同学  阅读(367)  评论(0编辑  收藏  举报