设计模式——结构性设计模式

结构性设计模式

针对类与对象的组织结构。(白话:类与对象之间的交互的多种模式

类/对象适配器模式

当需要传入一个A类型参数,但只有B类型类时,就需要一个A类型的适配器装入B类的数据,来将B数据转成A类型,然后作为参数传入

适配器,在生活中又称转换器。现在的手机基本都割去了 3.5mm的耳机接口,此时只有有线耳机,要听歌就需要一个转换器将 3.5mm接口转成手机有的 type-c的接口

类适配器(不建议)

继承需要转变的类

//主方法
public class Main {
    public static void main(String[] args) {
        TestSupplier supplier = new TestSupplier();
        test( ? );   //我们没有35MM类型的手机接口,只有type-c的手机接口,那这里该填个type-c。所以需要一个转接口将35MM转为type-c接口
    }

    public static void test(typeC typec){   //现在我们需要调用test方法,但是test方法需要类型是typeC
        System.out.println("成功得到:"+typec.listen());
    }
}

//接口
public interface typeC {    //typeC接口也想听歌
    String listen();
}

//父类
public class 35MM{
    public String listenMusic(){
        return "有线耳机听歌!"    //因为只有有线耳机,所以只有35MM才能听歌
    }
}

//子类作适配器 继承35MM,实现type-C接口
public class Adapter extends 35MM implements typeC{

    @Override
    public String listen() {  //现在不再继承35MM,仅实现typeC接口
        return super.listenMusic();
    }
}

对象适配器

将需要转变的类实例化,并用作与适配器类的构造方法

因为类适配器会占用一个继承位,而java又是单继承的。如果typeC不是接口而是抽象类的话就用不了了。所以提出对象适配器:

//主方法
public class Main {
    public static void main(String[] args) {
        TestSupplier supplier = new TestSupplier();
        test( ? );   //我们没有35MM类型的手机接口,只有type-c的手机接口,那这里该填个type-c。所以需要一个转接口将35MM转为type-c接口
    }

    public static void test(typeC typec){   //现在我们需要调用test方法,但是test方法需要类型是typeC
        System.out.println("成功得到:"+typec.listen());
    }
}

//接口
public interface typeC {    //typeC接口也想听歌
    String listen();
}

//父类
public class 35MM{
    public String listenMusic(){
        return "有线耳机听歌!"    //因为只有有线耳机,所以只有35MM才能听歌
    }
}

//子类作适配器 继承35MM,实现type-C接口
public class Adapter implements typeC{  //现在不再继承35MM,仅实现typeC接口
    35MM 35mm;  //实例化需要转变的类
    public Adapter(35MM 35mm){  //将实例化的对象用于构造对象
        this.35mm = 35mm;
    }

    @Override
    public String listen() {   //接着实现listen方法,直接使用typeC提供的实现
        return 35mm.listenMusic();
    }
}

桥接模式

配置自定义

同一种产品有着不同的配置,就像手机有:运行内存 4 6 8g,存储内存:64 128 256g,芯片:骁龙 A系列 麒麟 联发科 猎户座。不能每一种配置都写一个类就太麻烦了,所以有了桥接模式,可以通过多个类桥接成一个产品类。

优势:可以通过多个维度来自由设定配置

这里以华为手机举例:(小知识——华为手机是用自家的麒麟芯片)

//第一层类:继承该类可以自定义芯片类型
public abstract class AbstractPhone {
    private Size size; //这里是描述存储内存。由于举例简单点方便看得懂就不写运行内存了

    public AbstractPhone(Size size){
        this.size = size;
    }

    public abstract String getType();   //这里的类型是指芯片类型
}

//接口及实现类
public interface Size{
    String getSize();
}
public class 256G implements Size{
    @Override
    public String getSize() {
        return "256g内存";
    }
}

//第二层类:继承该类可以自定义芯片类型和存储内存的尺度大小
public abstract class RefinedAbstractPhone extends AbstractPhone{
    protected RefinedAbstractPhone(Size size) {
        super(size);
    }

    public String getSize(){   //添加尺寸维度获取方式
        return size.getSize();
    }
}

//产品类:继承第二层类,然后自定义存储内存大小和芯片种类
public class HUAWEI extends RefinedAbstractPhone{
    protected HUAWEI(Size size){    //构造方法指定具体存储内存大小
        super(size);
    }

    @Override
    public String getType() {
        return "华为手机";   //返回手机品牌类型
    }
}

//主方法
public static void main(String[] args) {
    HUAWEI huawei = new HUAWEI(new 256G());
    System.out.println(huawei.getType());
    System.out.println(huawei.getSize());
}

组合模式

对多个组件进行统一一样的操作

组合模式实际上就是将多个组件进行组合,让用户可以对它们进行一致性处理。比如我们的文件夹,一个文件夹中可以有很多个子文件夹或是文件。

它就像是一个树形结构一样,有分支有叶子,而组合模式则是可以对整个树形结构上的所有节点进行递归处理,比如我们现在希望将所有文件夹中的文件的名称前面都添加一个前缀,那么就可以使用组合模式。

设计模式——结构性设计模式

组合模式的示例如下,这里我们就用文件和文件夹的例子来讲解:

/**
 * 首先创建一个组件抽象,组件可以包含组件,组件有自己的业务方法
 */
public abstract class Component {
    public abstract void addComponent(Component component);    //添加子组件
    public abstract void removeComponent(Component component);   //删除子组件
    public abstract Component getChild(int index);   //获取子组件
    public abstract void test();   //执行对应的业务方法,比如修改文件名称
}

接着我们来编写两种实现类:文件夹实现类,文件实现类

public class Directory extends Component{   //目录可以包含多个文件或目录

    List child = new ArrayList<>();   //这里我们使用List来存放目录中的子组件

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

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

    @Override
    public Component getChild(int index) {
        return child.get(index);
    }

    @Override
    public void test() {
        child.forEach(Component::test);   //将继续调用所有子组件的test方法执行业务
    }
}
public class File extends Component{   //文件就相当于是树叶,无法再继续添加子组件了

    @Override
    public void addComponent(Component component) {
        throw new UnsupportedOperationException();   //不支持这些操作了
    }

    @Override
    public void removeComponent(Component component) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Component getChild(int index) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void test() {
        System.out.println("文件名称修改成功!"+this);   //具体的名称修改操作
    }
}

最后,我们来测试一下:可以看到我们对最外层目录进行操作后,会递归向下处理当前目录和子目录中所有的文件

public static void main(String[] args) {
    Directory outer = new Directory();   //新建一个外层目录
    Directory inner = new Directory();   //新建一个内层目录
    outer.addComponent(inner);
    outer.addComponent(new File());   //在内层目录和外层目录都添加点文件,注意别导错包了
    inner.addComponent(new File());
    inner.addComponent(new File());
    outer.test();    //开始执行文件名称修改操作
}

装饰模式

通过B类 实现对A类方法执行前后,分别多执行一些操作。类似于AOP

适用:业务功能前后实现一些操作。如:在支付前提醒是否需要支付xxx元。

//顶层抽象类
public abstract class Base {   //顶层抽象类,定义了一个test方法执行业务
    public abstract void test();
}

//业务实现类
public class BaseImpl extends Base{
    @Override
    public void test() {
        System.out.println("我是业务方法");   //具体的业务方法
    }
}

//装饰业务类(这里的构造方法参数是需要传入实现业务类对象)
public class Decorator extends Base{   //装饰者需要将装饰目标组合到类中

    protected Base base;

    public Decorator(Base base) {
        this.base = base;
    }

    @Override
    public void test() {
        base.test();    //这里暂时还是使用目标的原本方法实现
    }
}

//具体实现装饰业务类
public class DecoratorImpl extends Decorator{   //装饰实现

    public DecoratorImpl(Base base) {
        super(base);
    }

    @Override
    public void test() {    //对原本的方法进行装饰,我们可以在前后都去添加额外操作
        System.out.println("装饰方法:我是操作前逻辑");
        super.test();
        System.out.println("装饰方法:我是操作后逻辑");
    }
}

//主方法
public static void main(String[] args) {
    Base base = new BaseImpl();
    Decorator decorator = new DecoratorImpl(base);  //将Base实现装饰一下
    Decorator outer = new DecoratorImpl(decorator);  //装饰者还可以嵌套,此时是装饰两次

    decorator.test();   //装饰一次:装饰前——业务方法——装饰后

    outer.test();   //装饰两次:装饰前——装饰前——业务方法——装饰后——装饰后
}

代理模式

和装饰模式代码一模一样,但核心是思想不同

装饰模式和代理模式:

  1. 结构相同:都实现同一个接口/抽象类
  2. 作用不同:
  3. 装饰器模式强调的是增强自身,在被装饰之后你能够在被增强的类上使用增强后的功能,增强后你还是你,只不过被强化了而已;
  4. 代理模式强调要让别人帮你去做事情,以及添加一些本身与你业务没有太多关系的事情(记录日志、设置缓存等)重点在于让别人帮你做。

代理模式一般代码:

//顶层抽象类
public abstract class Base {   //顶层抽象类,定义了一个test方法执行业务
    public abstract void test();
}

//业务实现类
public class BaseImpl extends Base{
    @Override
    public void test() {
        System.out.println("我是业务方法");   //具体的业务方法
    }
}

//代理业务类(这里的构造方法参数是需要传入实现业务类对象)
public class Decorator extends Base{   //代理者需要将代理目标组合到类中

    protected Base base;

    public Decorator(Base base) {
        this.base = base;
    }

    @Override
    public void test() {
        base.test();    //这里暂时还是使用目标的原本方法实现
    }
}

//具体实现代理业务类
public class DecoratorImpl extends Decorator{   //代理实现

    public DecoratorImpl(Base base) {
        super(base);
    }

    @Override
    public void test() {    //对原本的方法进行代理,我们可以在前后都去添加额外操作
        System.out.println("装饰方法:我是操作前逻辑");
        super.test();
        System.out.println("装饰方法:我是操作后逻辑");
    }
}

//主方法
public static void main(String[] args) {
    Base base = new BaseImpl();
    Decorator decorator = new DecoratorImpl(base);  //将Base实现代理一下
    Decorator outer = new DecoratorImpl(decorator);  //代理者还可以嵌套,此时是代理两次

    decorator.test();   //代理一次:代理前——业务方法——代理后

    outer.test();   //代理两次:代理前——代理前——业务方法——代理后——代理后
}

实现代理模式除了和装饰模式一样的代码情况外还有两种实现方式:【因为都是动态代理所以生成的代理类是看不到的】

  1. JDK提供的动态代理:我们不再需要手动编写继承关系创建代理类,它能够在运行时通过反射机制为我们自动生成代理类:【只能代理接口】
//接口
public interface Subject {  //JDK提供的动态代理只支持接口
    void test();
}

//接口实现类
public class SubjectImpl implements Subject{

    @Override
    public void test() {
        System.out.println("我是测试方法!");
    }
}

//创建动态代理的处理逻辑(就是执行业务前后的方法编写在里面)
public class TestProxy implements InvocationHandler {    //代理类,需要实现InvocationHandler接口

    private final Object object;   //这里需要保存一下被代理的对象,下面需要用到

    public TestProxy(Object object) {
        this.object = object;
    }

    @Override   //此方法就是调用代理对象的对应方法时会进入,这里我们就需要编写如何进行代理了
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //method就是调用的代理对象的哪一个方法,args是实参数组
        System.out.println("代理的对象:"+proxy.getClass());   //proxy就是生成的代理对象了,我们看看是什么类型的
        Object res = method.invoke(object, args);   //在代理中调用被代理对象原本的方法,因为你是代理,还是得执行一下别人的业务,当然也可以不执行,但是这样就失去代理的意义了,注意要用上面的object
        System.out.println("方法调用完成,返回值为:"+res);   //看看返回值是什么
        return res;   //返回返回值
    }
}
  1. Spring在使用的CGLib框架代理。 maven依赖:

    cglib
    cglib
    3.1

代码实现:

//接口
public interface Subject {  //JDK提供的动态代理只支持接口
    void test();
}

//接口实现类
public class SubjectImpl implements Subject{

    @Override
    public void test() {
        System.out.println("我是测试方法!");
    }
}

//创建动态代理的处理逻辑(就是执行业务前后的方法编写在里面)
public class TestProxy implements MethodInterceptor {  //首先还是编写我们的代理逻辑

    private final Object target;   //这些和之前JDK动态代理写法是一样的

    public TestProxy(Object target) {
        this.target = target;
    }

    @Override   //我们也是需要在这里去编写我们的代理逻辑
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("现在是由CGLib进行代理操作!"+o.getClass());
        return method.invoke(target, objects);   //也是直接调用代理对象的方法即可
    }
}

//主方法
public static void main(String[] args) {
    SubjectImpl subject = new SubjectImpl();

    Enhancer enhancer = new Enhancer();   //增强器,一会就需要依靠增强器来为我们生成动态代理对象
    enhancer.setSuperclass(SubjectImpl.class);    //直接选择我们需要代理的类型,直接不需要接口或是抽象类,SuperClass作为代理类的父类存在,这样我们就可以按照指定类型的方式去操作代理类了
    enhancer.setCallback(new TestProxy(subject));  //设定我们刚刚编写好的代理逻辑

    SubjectImpl proxy = (SubjectImpl) enhancer.create();   //直接创建代理类

    proxy.test();   //调用代理类的test方法
}

外观模式

可以理解为门面模式,将需要通过操作多个类实现的一个功能封装到一个类中,便于使用

当每个功能是一个系统,完成一个业务需要多个功能时就需要分别调用多个系统,此时就可以将一个业务需要使用的多个系统封装成一个门面系统,只要调用该门面系统即可完成该业务。

设计模式——结构性设计模式

举例:比如现在我们设计了三个子系统,分别是排队、结婚、领证,正常情况下我们是需要分别去找这三个部门去完成的,但是现在我们通过门面统一来完成

//系统一
public class SubSystemA {
    public void test1(){
        System.out.println("排队");
    }
}

//系统二
public class SubSystemB {
    public void test2(){
        System.out.println("结婚");
    }
}

//系统三
public class SubSystemC {
    public void test3(){
        System.out.println("领证");
    }
}

//门面
public class Facade {

    SubSystemA a = new SubSystemA();
    SubSystemB b = new SubSystemB();
    SubSystemC c = new SubSystemC();

    public void marry(){   //红白喜事一条龙服务
        a.test1();
        b.test2();
        c.test3();
    }
}

//主方法
public static void main(String[] args) {
    Facade facade = new Facade();
    facade.marry();
}

享元模式

核心是共享。当A类方法里写了一个方法,B类中需要同样的方法就可以直接创建A类对象来调用方法或者通过一个方法工厂类收集各个方法,然后B类通过工厂类调用A类方法

举例:通过享元工厂类实现共享方法

//A类
public class DBUtil {
    public void selectDB(){
        System.out.println("我是数据库操作...");
    }
}

//享元工厂
public class DBUtilFactory {
    private static final DBUtil UTIL = new DBUtil();   //享元对象被存放在工厂中

    public static DBUtil getFlyweight(){   //获取享元对象
        return UTIL;
    }
}

//B类
public class UserService {   //用户服务

    public void service(){
        DBUtil util = DBUtilFactory.getFlyweight();   //通过享元工厂拿到DBUtil对象
        util.selectDB();    //该干嘛干嘛
    }
}

Original: https://www.cnblogs.com/buchizicai/p/16580751.html
Author: 不吃紫菜
Title: 设计模式——结构性设计模式

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/578692/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • vue 中,echarts的使用,简单入门

    vue 中,echarts的使用,简单入门 原作者哔哩哔哩视频 感谢 多多支持效果图 首先创建一个页面组件,创建三个div,分别来使用折线图,柱状图,扇形图 //折线图 //柱状图…

    Linux 2023年6月7日
    0114
  • Linux命令1

    快捷键 1&#x3001;&#x6253;&#x5F00;&#x7EC8;&#x7AEF; ctrl+alt+t 2&#x3001;…

    Linux 2023年6月8日
    0113
  • VS2022编译太慢

    解决方法是把编译出的exe程序或目录添加到杀毒软件白名单 一个C++的helloworld,在vs里硬是10秒才能编译启动。不知道大家有没有遇到。禁用符号加载还是很慢。甚至换成co…

    Linux 2023年6月6日
    0111
  • Nginx笔记

    实现负载均衡 这里采用的是权重 进入配置文件目录cd /usr/local/nginx/conf/ //实际根据自己的目录来 编辑vim nginx.conf 根据需要在此代码的顶…

    Linux 2023年5月27日
    0102
  • 安卓逆向从0到1学习总结

    PS:该文已经首发于公众号信安之路!!! 初识安卓逆向是在2019年的暑假,到现在也快一年了,这一年来有刚从web渗透转来的迷茫,有成功破解了第一个app的喜悦,也有通宵熬夜逆向的…

    Linux 2023年6月8日
    0120
  • ERROR: Exception when publishing, exception message [Failed to connect and initialize SSH connection

    jenkins 在构建时连接其他部署节点的服务器时报错,ERROR: Exception when publishing, exception message [Failed to…

    Linux 2023年6月14日
    093
  • nginx源码层面探究request_time、upstream_response_time、upstream_connect_time与upstream_header_time指标具体含义与区别

    背景概述 最近计划着重分析一下线上各api的HTTP响应耗时情况,检查是否有接口平均耗时、99分位耗时等相关指标过大的情况,了解到nginx统计请求耗时有四个指标:request_…

    Linux 2023年6月6日
    0103
  • redis 入门安装流程

    redis安装流程 安装linux的Redis [官网下载即可][ https://redis.io/download/ ] 一般会移动到opt目录下 mv redis-7.0.4…

    Linux 2023年6月7日
    0104
  • 数据结构和算法的关系

    针对Python数据结构与算法(裘宗燕版)中的第一章绪论最后的问题 数据结构 概念 数据与数据之间的结构关系(数组、队列、树、图等结构) 类别 分为 逻辑数据结构和 存储数据结构两…

    Linux 2023年6月7日
    0139
  • Python 定义类时候加括号和不加括号的区别

    新式类与经典类 只有python2.x 中有新式类和经典类的说法,而python3.x 没有,因为其默认都是新式类 python2.x 中默认都是经典类,只有显式的继承了objec…

    Linux 2023年6月7日
    074
  • 监控平台SkyWalking9入门实践

    简便快速的完成对分布式系统的监控; 一、业务背景 微服务作为当前系统架构的主流选型,虽然可以应对复杂的业务场景,但是随着业务扩展,微服务架构本身的复杂度也会膨胀,对于一些核心的业务…

    Linux 2023年6月14日
    092
  • PTA 《基础编程题目集》 6-7 统计某类完全平方数

    本题要求实现一个函数,判断任一给定整数N是否满足条件:它是完全平方数,又至少有两位数字相同,如144、676等。 函数接口定义: int IsTheNumber ( const i…

    Linux 2023年6月8日
    0116
  • jdk8 线程池策略

    在ThreadPoolExecutor中提供了4种线程的策略可以供开发者直接使用:•AbortPolicy策略:默认策略,如果线程池队列满了丢掉这个任务并且抛出RejectedEx…

    Linux 2023年6月8日
    0119
  • 工作三年的一些感悟

    前言 很久没有上博客,我是看着其中一篇文章进来,然后正好我也加起来三年,那就提笔写一下感触,出来三年基本上和有些同学断了联系,唯有室友还偶尔还会聊上几句,三年做过游戏测试、社交AP…

    Linux 2023年6月8日
    0103
  • Windows 10 多用户同时远程登录

    win服务器版默认是支持多用户登陆的,甚至可以在主机上用不同用户自己远程登陆自己,如window server 2016。 Win10 正常情况下是不允许用户同时远程的,即一个用户…

    Linux 2023年6月14日
    0141
  • 备用链接

    win10 stick note 地址 https://www.partitionwizard.com/partitionmanager/where-are-sticky-note…

    Linux 2023年6月7日
    090
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球