设计模式-结构型模式

设计模式-结构型模式

适配器模式(Adapter)

适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。

问题

假如你正在开发一款股票市场监测程序, 它会从不同来源下载 XML 格式的股票数据, 然后向用户呈现出美观的图表。

在开发过程中, 你决定在程序中整合一个第三方智能分析函数库。 但是遇到了一个问题, 那就是分析函数库只兼容 JSON 格式的数据。

整合分析函数库之前的程序结构

你无法 “直接” 使用分析函数库, 因为它所需的输入数据格式与你的程序不兼容。

你可以修改程序库来支持 XML。 但是, 这可能需要修改部分依赖该程序库的现有代码。 甚至还有更糟糕的情况, 你可能根本没有程序库的源代码, 从而无法对其进行修改。

解决方案

你可以创建一个适配器。 这是一个特殊的对象, 能够转换对象接口, 使其能与其他对象进行交互。

适配器模式通过封装对象将复杂的转换过程隐藏于幕后。 被封装的对象甚至察觉不到适配器的存在。 例如, 你可以使用一个将所有数据转换为英制单位 (如英尺和英里) 的适配器封装运行于米和千米单位制中的对象。

适配器不仅可以转换不同格式的数据, 其还有助于采用不同接口的对象之间的合作。 它的运作方式如下:

  1. 适配器实现与其中一个现有对象兼容的接口。
  2. 现有对象可以使用该接口安全地调用适配器方法。
  3. 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象。

有时你甚至可以创建一个双向适配器来实现双向转换调用。

适配器解决方案

让我们回到股票市场程序。 为了解决数据格式不兼容的问题, 你可以为分析函数库中的每个类创建将 XML 转换为 JSON 格式的适配器, 然后让客户端仅通过这些适配器来与函数库进行交流。 当某个适配器被调用时, 它会将传入的 XML 数据转换为 JSON 结构, 并将其传递给被封装分析对象的相应方法。

适配器有两种模式

类的适配器模式

image-20210526133903610

我们需要的接口:

// xml数据处理接口
public interface XmlHandle {

    // 处理xml数据方法
    void XmlProcess(String XMLdata);
}

别人提供的接口:

// json数据处理接口
public class JsonHandle {
    
    // 处理json数据方法
    void JsonProcess(String jsonData){
        System.out.println("处理json数据");
    }
}

适配器:

// 适配器
public class Adapter extends JsonHandle implements XmlHandle{

    @Override
    public void XmlProcess(String xmlData) {
        // 对数据进行加工
        this.JsonProcess(xmlData);
    }
}

对象的适配器模式

image-20210526134018539

我们需要的接口:

// xml数据处理接口
public interface XmlHandle {

    // 处理xml数据方法
    void XmlProcess(String XMLdata);
}

别人提供的接口:

// json数据处理接口
public class JsonHandle {
    
    // 处理json数据方法
    void JsonProcess(String jsonData){
        System.out.println("处理json数据");
    }
}

适配器:

// 适配器
public class Adapter  implements XmlHandle {

    private JsonHandle jsonHandle;

    // 构造方法
    public Adapter(JsonHandle jsonHandle) {
        this.jsonHandle = jsonHandle;
    }

    @Override
    public void XmlProcess(String xmlData) {
        // 对数据进行加工
        this.jsonHandle.JsonProcess(xmlData);
        
    }
}

装饰模式(Decorator)

装饰者模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰者来包裹真实的对象。

所以装饰者可以动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的方案。

装饰者模式组成结构

  • 抽象构件 (Component):给出抽象接口或抽象类,以规范准备接收附加功能的对象。
  • 具体构件 (ConcreteComponent):定义将要接收附加功能的类。
  • 抽象装饰 (Decorator):装饰者共同要实现的接口,也可以是抽象类。
  • 具体装饰 (ConcreteDecorator):持有一个 Component 对象,负责给构件对象“贴上”附加的功能。

image-20210526143049557

抽象构件:

// 抽象构件
public interface Component {

    // 功能
    void method();
}

具体构件:

// 具体构件
public class ConcreteComponent implements Component{

    // 具体构件功能的实现
    @Override
    public void method() {
        System.out.println("ConcreteComponent");
    }
}

抽象装饰 :

// 抽象装饰
public abstract class ConcreteDecorator implements Component{

    protected Component component;

    // 构造方法
    public ConcreteDecorator(Component component) {
        this.component = component;
    }
}

具体装饰:

// 具体装饰
public class Decorator  extends ConcreteDecorator{

    // 构造方法
    public Decorator(Component component) {
        super(component);
    }

    // 增强功能
    @Override
    public void method() {
        // 添加功能
        this.component.method();
    }
}

装饰模式的使用:

public class Main {
    
    public static void main(String[] args) {
        Component component = new Decorator(new ConcreteComponent());
        component.method();
    }
}

代理模式(Proxy)

代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问。

代理模式的结构

代理模式的主要角色如下。

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

image-20210526145645187

抽象主题类:

// 抽象主题
public interface Subject {

    // 需求要的方法
    void method();
}

真实主题类:

// 真实主题
public class RealSubject implements Subject{

    // 需求的具体实现
    @Override
    public void method() {
        System.out.println("method");
    }
}

代理类:

// 代理类
public class ProxySubject implements Subject{

    private RealSubject realSubject;

    // 构造函数
    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    // 代理的方法
    @Override
    public void method() {
        this.preMethod();
        this.realSubject.method();
        this.postMethod();
    }

    // 代理方法前的处理
    public void preMethod(){
        System.out.println("preMethod");
    }

    // 代理方法后的处理
    public void postMethod(){
        System.out.println("postMethod");
    }
}

外观模式(Facade)

外观模式是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口。

外观模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。门面模式又称为外观模式,它是一种对象结构型模式。

img

system是facade下的子系统

我们假设小成的爷爷已经80岁了,一个人在家生活:每次都需要打开灯、打开电视、打开空调;睡觉时关闭灯、关闭电视、关闭空调;

子系统可以表示为,灯系统,电视系统,热水器系统

//灯类系统
public class SubSystemA_Light {
    public void on() {
        System.out.println("打开了灯....");
    }

    public void off() {
        System.out.println("关闭了灯....");
    }
}
//电视类系统
public class SubSystemB_Television {
    public void on() {
        System.out.println("打开了电视....");
    }

    public void off() {
        System.out.println("关闭了电视....");
    }
}
//空调类系统
public class SubSystemC_Aircondition {
    public void on() {
        System.out.println("打开了电视....");
    }

    public void off() {
        System.out.println("关闭了电视....");
    }
}

通过外观类进行统一控制

public class Facade {

    SubSystemA_Light light;
    SubSystemB_Television television;
    SubSystemC_Aircondition aircondition;
    
    //传参
    public Facade(SubSystemA_Light light, SubSystemB_Television television, SubSystemC_Aircondition aircondition) {
        this.light = light;
        this.television = television;
        this.aircondition = aircondition;
    }

    //起床后一键开电器
    public void on() {
        System.out.println("起床了");
        light.on();
        television.on();
        aircondition.on();

        //睡觉时一键关电器
        System.out.println("睡觉了");
        light.off();
        television.off();
        aircondition.off();
    }
}

桥接模式(Bridge)

桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化

  • 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。
  • 修正抽象化(RefinedAbstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
  • 实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
  • 具体实现化(ConcreteImplementor)角色:这个角色给出实现化角色接口的具体实现。

星巴克提供了多种选择,从容量上说有大杯,中杯,小杯,从口味上说有加奶,加糖

首先定义咖啡加口味接口

// 咖啡加口味接口
public interface ICoffeeAdditives {
    
    void addSomething();
}

加奶实现类

// 加奶
public class Milk implements ICoffeeAdditives {
    
    @Override
    public void addSomething() {
        System.out.println("加奶");
    }
}

加糖实现类

// 加糖
public class Sugar implements ICoffeeAdditives {
    
    @Override
    public void addSomething() {
        System.out.println("加糖");
    }
}

抽象化Abstraction

// 咖啡抽象类
public abstract class Coffee {

    // 口味接口对象
    protected ICoffeeAdditives additives;

    public Coffee(ICoffeeAdditives additives){
        this.additives=additives;
    }

    public abstract void orderCoffee(int count);
}

// 抽象化修正对象

// 具体咖啡实现类
public abstract class RefinedCoffee extends Coffee {

    public RefinedCoffee(ICoffeeAdditives additives) {
        super(additives);
    }

    public void checkQuality(){
        Random ran=new Random();
        System.out.println(String.format("%s 添加%s",additives.getClass().getSimpleName(),ran.nextBoolean()?"太多":"正常"));
    }
}

组合模式(Composite)

组合模式是一种结构型设计模式, 你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们。

Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。

Leaf(叶子构件):它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。

Composite(容器构件):它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。

image-20210601131328782

以文件夹举例

抽象构件:

// 文件抽象接口
public abstract class Component {

    public String getName() {
        throw new UnsupportedOperationException("不支持获取名称操作");
    }

    public void add(Component component) {
        throw new UnsupportedOperationException("不支持添加操作");
    }

    public void remove(Component component) {
        throw new UnsupportedOperationException("不支持删除操作");
    }

    public void print() {
        throw new UnsupportedOperationException("不支持打印操作");
    }

    public String getContent() {
        throw new UnsupportedOperationException("不支持获取内容操作");
    }
}

叶子构件:

// 文件夹实现类
public class Composite extends Component {
    private String name;
    private List<Component> componentList = new ArrayList<Component>();

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

    @Override
    public String getName() {
        return this.name;
    }

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

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

    @Override
    public void print() {
        System.out.println(this.getName());
        for (Component component : this.componentList) {
            component.print();
        }
    }
}

容器构件:

// 文件实现类
public class Leaf extends Component{
    private String name;
    private String content;

    public Leaf(String name, String content) {
        this.name = name;
        this.content = content;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void print() {
        System.out.println(this.getName());
    }

    @Override
    public String getContent() {
        return this.content;
    }
}

享元模式(Flyweight)

享元模式是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。

Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。

ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。

UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。

FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。

image-20210609171736907

抽象享元类:

public interface FlyWeight {

    // 买书
    void sell();
}

具体享元类:

// 具体对象
public class BookOrder implements FlyWeight {

    private String name;

    BookOrder(String name) {
        this.name = name;
    }

    @Override
    public void sell() {
        System.out.println("卖了一本书,书名为'" + this.name + "'");
    }
}

享元工厂类:

// 享元工厂
public class FlyWeightFactory {

    // 共享池
    private Map<String, FlyWeight> bookPools = new HashMap<String, FlyWeight>();

    // 单例
    private static FlyWeightFactory factory = new FlyWeightFactory();
    
    public static FlyWeightFactory getInstance(){
        return factory;
    }
    //添加订单
    public FlyWeight getOrder(String bookName){
        FlyWeight order = null;
        if (bookPools.containsKey(bookName)) {
            order = bookPools.get(bookName);
        }else{
            order = new BookOrder(bookName);
            bookPools.put(bookName, order);
        }
        return order;
    }
    
    public int getTotalObjects(){
        return bookPools.size();
    }

}
posted @ 2021-06-09 17:18  smplaces  阅读(68)  评论(0)    收藏  举报