7个结构型设计模式

适配器模式

有一次冬天,我电脑卡的要死,联系客服之后,客户让我插上电脑适配器,我一愣问适配器是什么东西?客服姐姐说:就是充电器哈~

这是我第一次接触适配器这个名称。

众所周知,民电大多都是220V的,但是我们手机可没有这么高的电压,要让小小手机承受这么高的电压都是充电器(适配器)的功劳。假设我们手机是5V电压

适配器的三个角色:

  • Target目标抽象类 : 期望接口 5V ---->在Target提供方法中进行适配
  • Adapter 适配器类 : 作为中转角色 适配器
  • Adaptee适配者类 : 需要转化的 220V
package adapter;

public interface AC5 {
    int outputAC5();
}
package adapter;

public class AC220 {
    public int outputAC220V(){
        int output = 220;
        System.out.println("电压220V");
        return output;
    }
}

适配器的三种形式如下:

类适配器

通过继承实现适配器功能:Adapter实现Target接口,并继承Adaptee,这样Adapter就具备两者的特性,进行相互转化

package adapter;

public class Adapter extends AC220 implements AC5 {
    @Override
    public int outputAC5() {
        int adapterInput = super.outputAC220V();
        int adapterOutput = adapterInput / 44;
        System.out.println("输入电压" + adapterInput +"输出电压 " + adapterOutput);
        return adapterOutput;
    }
}

对象适配器

通过组合实现适配器功能:Adapter实现Target接口,内部持有Adaptee,在Target规定接口内转化Adaptee

package adapter;

public class Adapter2 implements AC5{
    private AC220 ac220;
    public Adapter2(AC220 ac220){
        this.ac220 = ac220;
    }
    @Override
    public int outputAC5() {
        int adapterInput = ac220.outputAC220V();
        int adapterOutput = adapterInput / 44;
        System.out.println("输入电压" + adapterInput +"输出电压 " + adapterOutput);
        return adapterOutput;
    }
}

接口适配器

接口适配器不是说去适配某接口,而是选择性的副高某些方法,也叫做缺省适配器模式

桥接模式

csdn

组合模式

以公司的管理举例子,公司有许多个部门,部门下可能又分为很多组,每个组里还分为不同职业的人,前端后端测试运维等等,对于老板来说,今天要下达放假一天的通知,不管是通知某一个组还是某一个人,都是发送一封邮件今天放假,不可能因为组比人规模大,邮件的信息就不一样吧

组合模式包含3个部分:

  • 抽象构建 : 所有对象的共有方法和属性
  • 叶子节点 : 叶子节点对象
  • 组合节点 : 油叶子节点构成

组合模式的关键就是在于抽象构建,他可以代表叶子,也可以代表组合节点,使用者根本不用在意它是叶子还是组合节点,对它进行统一处理

在没有组合前,代码是这样的

package zuhe;
//为了突出核心框架代码,我们对杀毒过程的实现进行了大量简化
import java.util.*;

//图像文件类
class ImageFile {
    private String name;

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

    public void killVirus() {
        //简化代码,模拟杀毒
        System.out.println("----对图像文件'" + name + "'进行杀毒");
    }
}

//文本文件类
class TextFile {
    private String name;

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

    public void killVirus() {
        //简化代码,模拟杀毒
        System.out.println("----对文本文件'" + name + "'进行杀毒");
    }
}

//文件夹类
class Folder {
    private String name;
    //定义集合folderList,用于存储Folder类型的成员
    private ArrayList<Folder> folderList = new ArrayList<Folder>();
    //定义集合imageList,用于存储ImageFile类型的成员
    private ArrayList<ImageFile> imageList = new ArrayList<ImageFile>();
    //定义集合textList,用于存储TextFile类型的成员
    private ArrayList<TextFile> textList = new ArrayList<TextFile>();

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

    //增加新的Folder类型的成员
    public void addFolder(Folder f) {
        folderList.add(f);
    }

    //增加新的ImageFile类型的成员
    public void addImageFile(ImageFile image) {
        imageList.add(image);
    }

    //增加新的TextFile类型的成员
    public void addTextFile(TextFile text) {
        textList.add(text);
    }

    //需提供三个不同的方法removeFolder()、removeImageFile()和removeTextFile()来删除成员,代码省略

    //需提供三个不同的方法getChildFolder(int i)、getChildImageFile(int i)和getChildTextFile(int i)来获取成员,代码省略

    public void killVirus() {
        System.out.println("****对文件夹'" + name + "'进行杀毒");  //模拟杀毒

        //如果是Folder类型的成员,递归调用Folder的killVirus()方法
        for(Object obj : folderList) {
            ((Folder)obj).killVirus();
        }

        //如果是ImageFile类型的成员,调用ImageFile的killVirus()方法
        for(Object obj : imageList) {
            ((ImageFile)obj).killVirus();
        }

        //如果是TextFile类型的成员,调用TextFile的killVirus()方法
        for(Object obj : textList) {
            ((TextFile)obj).killVirus();
        }
    }
}
public class Client {
    public static void main(String args[]) {
        Folder folder1,folder2,folder3;
        folder1 = new Folder("Sunny的资料");
        folder2 = new Folder("图像文件");
        folder3 = new Folder("文本文件");

        ImageFile image1,image2;
        image1 = new ImageFile("小龙女.jpg");
        image2 = new ImageFile("张无忌.gif");

        TextFile text1,text2;
        text1 = new TextFile("九阴真经.txt");
        text2 = new TextFile("葵花宝典.doc");

        folder2.addImageFile(image1);
        folder2.addImageFile(image2);
        folder3.addTextFile(text1);
        folder3.addTextFile(text2);
        folder1.addFolder(folder2);
        folder1.addFolder(folder3);

        folder1.killVirus();
    }
}

在组合后

package zuhe;
import java.util.*;

//抽象文件类:抽象构件
abstract class AbstractFile {
    public abstract void add(AbstractFile file);
    public abstract void remove(AbstractFile file);
    public abstract AbstractFile getChild(int i);
    public abstract void killVirus();
}

//图像文件类:叶子构件
class ImageFile extends AbstractFile {
    private String name;

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

    public void add(AbstractFile file) {
        System.out.println("对不起,不支持该方法!");
    }

    public void remove(AbstractFile file) {
        System.out.println("对不起,不支持该方法!");
    }

    public AbstractFile getChild(int i) {
        System.out.println("对不起,不支持该方法!");
        return null;
    }

    public void killVirus() {
        //模拟杀毒
        System.out.println("----对图像文件'" + name + "'进行杀毒");
    }
}

//文本文件类:叶子构件
class TextFile extends AbstractFile {
    private String name;

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

    public void add(AbstractFile file) {
        System.out.println("对不起,不支持该方法!");
    }

    public void remove(AbstractFile file) {
        System.out.println("对不起,不支持该方法!");
    }

    public AbstractFile getChild(int i) {
        System.out.println("对不起,不支持该方法!");
        return null;
    }

    public void killVirus() {
        //模拟杀毒
        System.out.println("----对文本文件'" + name + "'进行杀毒");
    }
}

//视频文件类:叶子构件
class VideoFile extends AbstractFile {
    private String name;

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

    public void add(AbstractFile file) {
        System.out.println("对不起,不支持该方法!");
    }

    public void remove(AbstractFile file) {
        System.out.println("对不起,不支持该方法!");
    }

    public AbstractFile getChild(int i) {
        System.out.println("对不起,不支持该方法!");
        return null;
    }

    public void killVirus() {
        //模拟杀毒
        System.out.println("----对视频文件'" + name + "'进行杀毒");
    }
}

//文件夹类:容器构件
class Folder extends AbstractFile {
    //定义集合fileList,用于存储AbstractFile类型的成员
    private ArrayList<AbstractFile> fileList=new ArrayList<AbstractFile>();
    private String name;

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

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

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

    public AbstractFile getChild(int i) {
        return (AbstractFile)fileList.get(i);
    }

    public void killVirus() {
        System.out.println("****对文件夹'" + name + "'进行杀毒");  //模拟杀毒

        //递归调用成员构件的killVirus()方法
        for(Object obj : fileList) {
            ((AbstractFile)obj).killVirus();
        }
    }
}
public class Client2 {
    public static void main(String args[]) {
        //针对抽象构件编程
        AbstractFile file1,file2,file3,file4,file5,folder1,folder2,folder3,folder4;

        folder1 = new Folder("Sunny的资料");
        folder2 = new Folder("图像文件");
        folder3 = new Folder("文本文件");
        folder4 = new Folder("视频文件");

        file1 = new ImageFile("小龙女.jpg");
        file2 = new ImageFile("张无忌.gif");
        file3 = new TextFile("九阴真经.txt");
        file4 = new TextFile("葵花宝典.doc");
        file5 = new VideoFile("笑傲江湖.rmvb");

        folder2.add(file1);
        folder2.add(file2);
        folder3.add(file3);
        folder3.add(file4);
        folder4.add(file5);
        folder1.add(folder2);
        folder1.add(folder3);
        folder1.add(folder4);

        //从“Sunny的资料”节点开始进行杀毒操作
        folder1.killVirus();
    }
}

可以看到在组合节点中添加数据的时候我们没必要去说添加图片/文本,而是直接全部add,因为他们都属于AbstractFile

装饰器模式

装饰器的核心是功能扩展,动态且透明地对扩展类进行扩展,主要实现原理是

让装饰器实现被包装类相同的接口,并在构造函数中传入该接口对象,在接口需要实现的方法中被包装类对象的现有功能添加新功能,装饰器要和被包装类属于统一类型

装饰器的4个角色:

  • 抽象组件Component
  • 具体组件ConcreteCompnent :实现或者继承Component
  • 抽象装饰器Decorator : 内部含有一个属性指向Component,其实现一般是抽象类,为了让子类按照其构造形式传入一个Comoonent组件
  • 具体装饰器ConcreteDecorator:扩展Component

我们以一个煎饼加料为例子

Component

package decorator;

public abstract class Battercake {
    protected abstract String getMsg();
    protected abstract int getPrice();
}

ConcreteCompnent

package decorator;

public class BaseBattercake extends Battercake{
    @Override
    protected String getMsg() {
        return "煎饼";
    }

    @Override
    protected int getPrice() {
        return 5;
    }
}

Decorator

package decorator;

public abstract class BattercakeDecorator extends Battercake{
    private Battercake battercake;
    public BattercakeDecorator(Battercake battercake) {
        this.battercake= battercake;
    }
    @Override
    protected String getMsg() {
        return this.battercake.getMsg();
    }

    @Override
    protected int getPrice() {
        return this.battercake.getPrice();
    }
}

ConcreteDecorator

package decorator;

public class EggDecorator extends BattercakeDecorator{
    public EggDecorator(Battercake battercake) {
        super(battercake);
    }

    @Override
    public String getMsg() {
        return super.getMsg() + "鸡蛋";
    }

    @Override
    public int getPrice() {
        return super.getPrice() + 1;
    }
}

接下来,想在煎饼中加东西,直接写一个ConcreteDecorator类就好,可以无限加料

    public static void main(String[] args) {
        Battercake battercake;
        battercake = new BaseBattercake();
        battercake = new EggDecorator(battercake);
        battercake = new EggDecorator(battercake);
        System.out.println(battercake.getMsg() + battercake.getPrice());
    }

外观模式

外观模式也称为门面模式,这是一个解耦的模式,让我想起前几天去体检时候的经理,我是在网络上得知这家医院体检模式十分友好,体检的项目众多,到处都是窗口,导医会领着我们一步一步指导,应该去哪里做什么体检,而不是我们自行寻找窗口。门面模式也是如此,它通过引入外观类,为多个业务类调用提供统一接口,使用者并不需要自行寻找接口调用接口,而是外观类做了这些事情

如下,业务系统中的方法众多,abcde…等等方法

class SubSystemA
{
    public void MethodA()
    {
        //业务实现代码
    }
}
 
class SubSystemB
{
    public void MethodB()
    {
        //业务实现代码
     }
}
 
class SubSystemC
{
    public void MethodC()
    {
        //业务实现代码
    }
}

引入外观类后,子系统的业务类统一由外观类负责,用户只需要调用外观类中提供的线程方法即可,不用自行调用methodA,methodB,methodC了

class Facade
{
    private SubSystemA obj1 = new SubSystemA();
    private SubSystemB obj2 = new SubSystemB();
    private SubSystemC obj3 = new SubSystemC();
 
    public void Method()
    {
        obj1.MethodA();
        obj2.MethodB();
        obj3.MethodC();
    }
}

享元模式

当对象的数量过多,导致程序运行代价过高,性能下降的时候,享元模式应运而生。

它类似于线程池,线程池可以避免不断创建和销毁线程,享元模式也是如此,避免了不断创建和销毁多个对象,消耗性能。在享元模式中存储这些共享实例对象的地方称为享元池(Flyweight Pool)

享元模式把一个对象的状态分为内部状态和外部状态,内部状态不可变

享元模式包含4个角色

  • 抽象享元类:在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
  • 具体享元类:它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。
  • 享元工厂类:享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。

享元工厂类

class FlyweightFactory {

    //定义一个HashMap用于存储享元对象,实现享元池

       private HashMap flyweights = newHashMap();

      

       public Flyweight getFlyweight(String key){

              //如果对象存在,则直接从享元池获取

              if(flyweights.containsKey(key)){

                     return(Flyweight)flyweights.get(key);

              }

              //如果对象不存在,先创建一个新的对象添加到享元池中,然后返回

              else {

                     Flyweight fw = newConcreteFlyweight();

                     flyweights.put(key,fw);

                     return fw;

              }

       }

}

享元类:

class Flyweight {

     //内部状态intrinsicState作为成员变量,同一个享元对象其内部状态是一致的

       private String intrinsicState;

      

       public  Flyweight(String intrinsicState) {

              this.intrinsicState=intrinsicState;

       }

      

        //外部状态extrinsicState在使用时由外部设置,不保存在享元对象中,即使是同一个对象,在每一次调用时也可以传入不同的外部状态

       public void operation(String  extrinsicState) {

              ......

       }     

}

享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。

代理模式

简单说说代理模式,生活中的代理模式还是不少的,比如前几天租房被中介狠狠坑了

房东可以租房给我们,中介也可以,但是中介其实是一个代理人,因为房东不是经常有时间,但是中介就是干这个的。所以代理模式就是代理对象具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理。

代理又分为静态代理和动态代理

静态代理

代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是 Real Object.

/**
 * 需要静态代理的接口
 */
public interface Subject {
    public void inrtroduce(String name);
}
/**
 * 被代理的真实对象
 */
public class RealSubject implements Subject {
    @Override
    public String inrtroduce(String name) {
        System.out.println("My name is :"+name);
        return name;
    }
}
/**
 * 代理类
 */
public class InvocationHandlerIml implements Subject  {

    //代理的真实对象 基本已经写死
    private RealSubject subject;

    public InvocationHandlerIml(RealSubject subject) {
        this.subject = subject;
    }

    @Override
    public void inrtroduce(String name) {
        System.out.println("调用前的业务");
         subject.inrtroduce(name);
        System.out.println("调用后的业务");
    }
}

静态代理的方式存在一个最大的问题就是对目标方法的调用逻辑写死在代理类方法里面,每一个被代理类的方法都是不同的,需要创建同样个数的代理类才能实现对不同被代理类的调用。所以就有了动态代理

动态代理

JDk的动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。

/**
 * 需要动态代理的接口
 */
public interface Subject {
    public String inrtroduce(String name);
}

/**
 * 被代理的真实对象
 */
public class RealSubject implements Subject {
    @Override
    public String inrtroduce(String name) {
        System.out.println("My name is :"+name);
        return name;
    }
}

/**
 * 代理类
 */
public class InvocationHandlerIml implements InvocationHandler {

    //代理的真实对象 接口
    private Subject subject;


      //开始调度
    public  Object newproxy(Subject targetObject){
        subject=targetObject;
        ClassLoader classLoader = subject.getClass().getClassLoader();
        Class<?>[] interfaces = subject.getClass().getInterfaces();
        return Proxy.newProxyInstance(classLoader,interfaces,this);
    }

    //增强当前的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用前的业务");
        System.out.println("method"+method);
        Object result = method.invoke(subject, args);
        System.out.println("调用后的业务");
        return result;
    }
}

/**
 * 测试
 */
public class Test {
    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        InvocationHandlerIml invocationHandlerIml = new InvocationHandlerIml();
        Subject subject = (Subject)invocationHandlerIml.newproxy(realSubject);
        subject.inrtroduce("帅哥");
    }
}

感谢链接大佬的文章很不错

  • 13
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值