设计模式④ :分开考虑

news2024/11/25 16:42:33

一、前言

有时候不想动脑子,就懒得看源码又不像浪费时间所以会看看书,但是又记不住,所以决定开始写"抄书"系列。本系列大部分内容都是来源于《 图解设计模式》(【日】结城浩 著)。该系列文章可随意转载。

二、Bridge 模式

Bridge 模式 :将类的功能层析结构与实现层次结构分离。

1. 介绍

Bridge 模式的作用是将两种东西连接起来,它们分别是 类的功能层次结构 和 类的实现层次结构,但是通常来说,类的层次结构不应当过深。

  • 类的功能层次结构 :在父类中具有基本功能,在子类中增加新的功能。这种层次结构成为 类的功能层次结构。
  • 类的实现层次结构:父类通过声明抽象方法来定义接口,子类通过实现具体方法来实现接口。这种层次结构称为 类的实现层次结构。

Bridge 模式登场的角色

  • Abstraction (抽象化) : 该角色位于“类的功能层次结构”的最上层,他使用 Implementor 角色的方法定义了基本的功能。该角色保存了 Implementor 角色的实例。在示例程序中,由 Display 类扮演此角色。
  • RefineAbstraction (改善后的抽象化) :在 Abstraction 角色的基础上增加了新功能的角色。在示例程序中,由 CountDisplay 类扮演此角色。
  • Implementor (实现者) :该角色位于“类的实现层次结构”的最上层,他定义了用于实现 Abstraction 角色的接口的方法。在示例程序中由 DisplayImple 类扮演此角色。
  • ConcreteImplementor (具体实现者) :该角色负责实现在 Implementor 角色中定义的接口(API)。在实例程序中,由 StringDisplayImple 类扮演该角色。

类图如下,左侧的两个类构成了“类的功能层次结构”,右侧的两个类构成了“类的实现层次结构”。类的两个层次结构之间的桥梁是 impl 字段:
在这里插入图片描述


Demo 如下:

// 类的功能层次结构
public class Display {
    private DisplayImpl impl;

    public Display(DisplayImpl impl) {
        this.impl = impl;
    }

    public void open() {
        impl.rawOpen();
    }

    public void print() {
        impl.rawPrint();
    }

    public void close() {
        impl.rawClose();
    }
}
// 类的实现层次结构
public interface DisplayImpl {
    void rawOpen();

    void rawPrint();

    void rawClose();
}

// 类的实现层次结构
public class StringDisplayImpl implements DisplayImpl {
    @Override
    public void rawOpen() {
        System.out.println("StringDisplayImpl.rawOpen");
    }

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

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

// 类的功能层次结构
public class CountDisplay extends Display {
    public CountDisplay(DisplayImpl impl) {
        super(impl);
    }

    public void multiDisplay() {
        for (int i = 0; i < 3; i++) {
            print();
        }
    }
}

public class BridgeDemoMain {
    public static void main(String[] args) {
        Display display1 = new Display(new StringDisplayImpl());
        Display display2 = new CountDisplay(new StringDisplayImpl());
        display1.display();
        System.out.println("--------------------------------");
        display2.display();
        System.out.println("--------------------------------");
        ((CountDisplay)display2).multiDisplay();
    }
}

输出结果:

在这里插入图片描述
综上:通过 CountDisplay 类完成了对 Display 类的方法扩展,即类的功能层次结构扩展。通过 DisplayImpl 类完成了与 Display 的解耦,Display 将 open、print、close 方法委托给了 impl 来实现,即类的实现层次结构扩展。

2. 应用

  • Spring 中的 BeanPostProcessor 接口:在 Spring 中 Bean 创建前后会调用 BeanPostProcessor 的方法来对 Bean进行前置或后置处理,而 BeanPostProcessor 具有很多子接口,如 InstantiationAwareBeanPostProcessor 、MergedBeanDefinitionPostProcessor 等,其子接口都各自增加了自己的方法。如下接口定义:

    public interface BeanPostProcessor {
    	@Nullable
    	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    		return bean;
    	}
    	@Nullable
    	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    		return bean;
    	}
    
    }
    
    
    public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
    
    	@Nullable
    	default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    		return null;
    	}
    
    
    	default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
    		return true;
    	}
    
    	@Nullable
    	default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
    			throws BeansException {
    
    		return null;
    	}
    
    
    	@Deprecated
    	@Nullable
    	default PropertyValues postProcessPropertyValues(
    			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
    
    		return pvs;
    	}
    
    }
    


个人使用:该部分内容是写给自己看的,帮助自身理解,因此就不交代项目背景了,读者请自行忽略(◐ˍ◑)

  • 项目A中需要对文件进行解析,当时为了兼容各种文件,定义了文件解析委托类,并且在此基础上进行了扩展,如下,不同的文件类型基于自身的特性实现不同的接口来完成基础功能的解析:

    // 接口, 文件解析委托类
    public interface DocDelegate extends Closeable {
    
        /**
         * 读取全部内容
         *
         * @return
         */
        <K> K readContent();
    
        /**
         * 关闭
         *
         * @throws IOException
         */
        @Override
        default void close() {
    
        }
    }
    
    // 用于解析可以按行读取的文件,如 Excel
    public interface DocLineDelegate<T> extends DocDelegate {
        /**
         * 按行分割 内容
         *
         * @return
         */
        List<T> readLineContent();
    
    }
    
    // 用于解析可以按页读取的文件,如Word
    public interface DocPageDelegate<T> extends DocDelegate {
        /**
         * 获取当前页数
         *
         * @return
         */
        int getNumberOfPages();
    
        /**
         * 读取某一页的内容
         *
         * @param page
         * @return
         */
        T readPage(int page);
    
    }
    

3. 总结

Bridge 模式的特征是将“类的功能层次结构” 和“类的实现层次结构”分离开了。当想要增加功能时,只需要在“类的功能层次结构” 一侧增加类即可。不必对“类的实现层次结构”做任何修改,而且增加够的功能可以被“所有的实现”使用。

需要注意的是,虽然使用“继承”也很容易扩展类,但是类之前形成了一种强关联关系,如果需要修改类之前的关系,使用继承就不合适了,因为每次改变都需要修改原程序。这是便可以使用"委托"来代替“继承关系”。如上述Demo中,Display 将 open、print、close 方法委托给了 impl 来实现,如果需要改变关联关系,在创建 Display 时传入新的 Impl 实现即可。

三、Strategy 模式

Strategy 模式 : 整体地替换算法

1. 介绍

Strategy 模式登场的角色:

  • Strategy (策略):Strategy 角色负责决定实现策略所必需的接口(API)。
  • ConcreteStrategy(具体的策略):ConcreteStrategy角色负责实现 Strategy 角色的接口,即负责实现具体的策略。
  • Context(上下文):负责使用Strategy 角色。Context 角色保存了 ConcreteStrategy 角色的实例,并使用ConcreteStrategy 角色去实现需求。

类图如下:
在这里插入图片描述


Demo如下:

// 策略接口
public interface Strategy {
    int getNumber();
}

// 获取随机奇数
public class OddNumberStrategy implements Strategy {
    @Override
    public int getNumber() {
        int number;
        do {
            number = RandomUtils.nextInt();
        } while (number % 2 == 0);
        return number;
    }
}

// 获取随机偶数
public class EvenNumberStrategy implements Strategy {

    @Override
    public int getNumber() {
        int number;
        do {
            number = RandomUtils.nextInt();
        } while (number % 2 != 0);
        return number;
    }
}
//策略上下文,持有所有策略
public class StrategyContext {

    private EvenNumberStrategy evenNumberStrategy = new EvenNumberStrategy();

    private OddNumberStrategy oddNumberStrategy = new OddNumberStrategy();
    
    public int getEventNumber(){
        return evenNumberStrategy.getNumber();
    }  
    public int getOddNumber(){
        return oddNumberStrategy.getNumber();
    }
}

public class StrategyDemoMain {
    public static void main(String[] args) {
    	// 选择合适的策略执行
        StrategyContext strategyContext = new StrategyContext();
        final int oddNumber = strategyContext.getOddNumber();
        final int eventNumber = strategyContext.getEventNumber();
        System.out.println("oddNumber = " + oddNumber);
        System.out.println("eventNumber = " + eventNumber);
    }
}

2. 应用

  • 线程池的任务拒绝策略:在我们自定义线程时是需要传入一个任务拒绝处理器,Java默认提供了多种拒绝策略的实现,通过选择不同的策略可以在线程池满任务时选择对应的处理方式,如丢失任务、抛出异常等。如下,可以通过传入不同的 RejectedExecutionHandler 来实现不同的拒绝策略。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
    
    
  • Dubbo的负载均衡策略: Dubbo 通过 LoadBalance 接口完成负载均衡,而负载均衡有多种方案可以选择,如随机、轮询、按比重等等,Dubbo 对每种情况实现了各自的 LoadBalance ,然后根据配置选择合适的 LoadBalance 策略来完成负载均衡。

    	// AbstractLoadBalance 中,子类 LoadBalance 实现 doSelect 方法来实现自己的策略。
        @Override
        public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
            if (CollectionUtils.isEmpty(invokers)) {
                return null;
            }
            if (invokers.size() == 1) {
                return invokers.get(0);
            }
            return doSelect(invokers, url, invocation);
        }
    
        protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);
    


个人使用:该部分内容是写给自己看的,帮助自身理解,因此就不交代项目背景了,读者请自行忽略(◐ˍ◑)

  • 项目A 中需要对文件进行解析读取,对于同一份文件,在不同的用途时需要解析获取的数据也不同,此时会为每种目的建立不同的读取策略,通过不同的策略来获取不同的信息。

  • 项目B 中需要手动提供一个 TraceId 进行数据记录,对于这种情况肯定是通过AOP 完成,除此之外,为了普适性,额外提供了一个 TraceId 生成的策略类,如果某个项目需求不同,需要生成不同格式的 TraceId,则可以通过实现策略类来完成。如下:

    public interface TraceIdStrategy {
        /**
         * 获取 id
         *
         * @return
         */
        String getTraceId();
    
        /**
         * 获取id
         *
         * @param traceId
         * @return
         */
        String getTraceId(String traceId);
    }
    
    //提供一个默认的策略
    public class DefaultTraceIdStrategy implements TraceIdStrategy {
    
        @Override
        public String getTraceId() {
            return UUID.randomUUID().toString();
        }
    
        @Override
        public String getTraceId(String traceId) {
            return traceId;
        }
    }
    
    // 注入MDC 
    @Order(Integer.MIN_VALUE)
    public class MDCTraceConfigurer implements WebMvcConfigurer {
    
        private final TraceIdStrategy traceIdStrategy;
        
        public MDCTraceConfigurer(ObjectProvider<TraceIdStrategy> traceIdStrategyOp) {
            this.traceIdStrategy = traceIdStrategyOp.getIfAvailable(DefaultTraceIdStrategy::new);
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new MDCTraceInterceptor(traceIdStrategy)).addPathPatterns("/**");
        }
    }
    

3. 总结

相关设计模式:

  • Flyweight 模式 :有时会使用Flyweight 模式让多个地方可以共用 ConcreteStrategy 角色。
  • Abstract Factory 模式 :使用 Strategy 模式可以整体替换算法,使用 Abstract Factory 模式则可以整体地替换具体工厂、零件和产品
  • State 模式:使用Strategy 模式和 State模式都可以替换被委托对象,而且他们的类之间的关系也很相似。但是这两种模式的目的不同。Strategy 模式中 ConcreteStrategy 角色是代表算法的类。在 Strategy 模式中,可以替换被委托的对类。而在 State模式中,ConcreteState 角色是表示 “状态” 的类。在 State模式中,每次状态变化时,被委托对象的类都必定会被替换掉。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1366429.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小白入门基础 - Restful

一&#xff1a;REST与RESTful&#xff1a; REST&#xff1a;表现层状态转移&#xff0c;资源在网络中以某种形式进行状态转移。 RESTful是基于REST理念的一套开发风格&#xff0c;是具体的开发规则。 服务器端只返回数据&#xff0c;以json或者xml的格式。 RESTful开发规范&a…

小白新手轻松部署扫雷小游戏

小白新手轻松部署扫雷小游戏 云效云效操作导入资源镜像仓库应用配置 最后 说到扫雷小游戏&#xff0c;可以说大家都玩儿过&#xff0c;印象中刚接触计算机的时候&#xff0c;对于这个扫雷小游戏&#xff0c;很多人都很喜欢&#xff0c;觉得很有意思&#xff0c;大家一起挑战看谁…

win10报错“COMDLG32.OCX文件丢失,软件无法启动”,快速修复方法,亲测有效

COMDLG32.OCX文件是Windows操作系统中的一个ActiveX控件文件&#xff0c;是Common Dialogs控件的一部分&#xff0c;主要用于支持各种软件运行时的一些基本功能&#xff0c;如打开、保存文件对话框等。 COMDLG32.OCX文件的作用主要体现在以下几点&#xff1a; 1. 提供应用程序通…

Commander One for Mac:强大的双窗格文件管理器,让你的工作效率倍增!

Commander One for Mac是一款功能强大的文件管理工具&#xff0c;具有以下主要功能&#xff1a; 双窗格设计&#xff1a;主界面分为两个窗格&#xff0c;用户可以在左侧窗格中导航和浏览文件系统的目录结构&#xff0c;在右侧窗格中查看文件和文件夹的内容。文件操作&#xff…

【教学类-45-02】X-Y之间的三连减题(a-b-c=)

作品展示&#xff1a; 背景需求&#xff1a; 【教学类-45-01】X-Y之间的三连加题(abc)-CSDN博客文章浏览阅读5次。【教学类-45-01】X-Y之间的三连加题(abc)https://blog.csdn.net/reasonsummer/article/details/135436915 有了三连加怎么能没有三连减&#xff0c;修改参数&am…

Vmware安装Windows11系统及下载MySQL步骤(超详细)

一、创建虚拟机 ①选择自定义 ②直接点击下一步 ③选择Windows 11 x64 ④命名虚拟机以及选择路径 ⑤新版本的虚拟机需要加密&#xff08;密码需要8个字符以上&#xff09; ⑥选择UEFI ⑦处理器配置&#xff08;根据自己的需求&#xff09; ⑧设置虚拟机的内存 ⑨选择不使用网络…

LeetCode 2807. 在链表中插入最大公约数【链表,迭代,递归】1279

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

天锐绿盾文档加密系统

绿盾文档加密系统是一种针对企业数据安全而设计的解决方案&#xff0c;旨在保护企业核心数据不被泄露。该系统由文件加密模块、内网安全模块等部分组成&#xff0c;主要功能包括对需要保护的文件进行强制加密保护&#xff0c;并对文件的使用进行全程监控。通过绿盾文档加密系统…

数据结构之B树和B+树

数据结构可视化演示链接&#xff0c;也就是视频中的网址 文章目录 一、B-Tree二、BTree(B-Tree变种) 一、B-Tree 叶节点具有相同的深度&#xff0c;叶节点的指针为空所有索引元素不重复节点中的数据索引从左到右递增排列 二、BTree(B-Tree变种) 非叶子节点不存储 data &#…

如何进行深入的竞品分析:掌握这些技巧让你更加了解市场

随着互联网行业的快速发展&#xff0c;产品经理需要对竞品进行深入分析&#xff0c;才能更好地把握市场需求和趋势&#xff0c;为公司带来更好的商业价值。那么&#xff0c;如何做好竞品分析呢&#xff1f;以下是我对于这个问题的思考和建议。 一、确定分析的目的和范围 在开…

积分的求法

1.第一类换元积分法&#xff08;凑微分法&#xff09;&#xff1a;用dt代替dx&#xff0c;积分消失加常数 2.第二类换元积分法&#xff08;用于开根号&#xff09;&#xff1a;用sint,cost,tant代替x&#xff0c;其中tant可以用于secx的平方-1tanx的平方 3.倒代换&#xff1a…

10个常用恶意软件检测分析平台(网工精选)

你们好&#xff0c;我的网工朋友。 我们往期的文章已经同步过很多的好用工具&#xff0c;毕竟&#xff0c;作为一个职场网工人&#xff0c;提升效率才是才能在单位时间内赚到更多的工资 往期的好用工具收到了不少好的反响&#xff0c;今天更新一波新的工具安利&#xff0c;专…

Python实现PowerPoint(PPT/PPTX)到PDF的批量转换

演示文稿是一种常见传达信息、展示观点和分享内容的形式&#xff0c;特别是PowerPoint演示文稿&#xff0c;广泛应用于各行各业&#xff0c;几乎是演讲等场合的必备工具。然而&#xff0c;演示文稿也有其限制&#xff0c;对设备的要求较高&#xff0c;且使用不同的软件或设备演…

用户管理第2节课--idea 2023.2 后端--实现基本数据库操作(操作user表) -- 自动生成 --合并生成后的代码【鱼皮】

一、模块页面功能 1.1 domain 【实体对象】 1.2 mapper 【操作数据库的对象】--> UserMapper 1&#xff09;UserMapper 其实就是我们用来操作数据库的一个对象 2) 继承了mybatis- plus&#xff0c;它会自动帮我们去定义一些增删改查的方法。 继承可以看下图&#xf…

1.4~1.5链表复习,代码操作(反转链表(用栈解决,双指针),删除链表指定元素),链表选择题,广义表

删除链表内指定范围的数 思路是双指针&#xff0c;定义两个指针&#xff0c;一个去找当前这个数满不满足要求&#xff0c;然后另一个定义为删除区间的起点 &#xff0c; 当不满足时&#xff0c;两个指针同时向后移动&#xff1b;当满足时&#xff0c;前驱指针就不动了&#xf…

决策树--CART分类树

1、介绍 &#xff08;1&#xff09;简介 CART&#xff08;Classification and Regression Trees&#xff09;分类树是一种基于决策树的机器学习算法&#xff0c;用于解 决分类问题。它通过构建树状的决策规则来对数据进行分类。 &#xff08;2&#xff09;生成过程 ① 选择…

AR眼镜定制_ar智能眼镜5G硬件解决方案

AR眼镜近年来备受瞩目&#xff0c;其易于佩戴、轻巧耐用、功能强大、用途广泛的特点受到了广泛关注。 AR眼镜的应用场景非常广泛&#xff0c;不仅包括消费级市场&#xff0c;还涵盖了旅游、教育、工业、医疗等多个领域。新的工业AR穿戴技术以及工业级语音交互操作系统&#xff…

2020年认证杯SPSSPRO杯数学建模D题(第二阶段)让电脑桌面飞起来全过程文档及程序

2020年认证杯SPSSPRO杯数学建模 D题 让电脑桌面飞起来 原题再现&#xff1a; 对于一些必须每天使用电脑工作的白领来说&#xff0c;电脑桌面有着非常特殊的意义&#xff0c;通常一些频繁使用或者比较重要的图标会一直保留在桌面上&#xff0c;但是随着时间的推移&#xff0c;…

【css】快速实现鼠标悬浮变色效果

<div class"nav-item"><div class"ic-img"></div><div>切换</div> </div>.nav-item {width: 100rem;height: 45rem;line-height: 45rem;display: flex;text-align: center;justify-content: center;align-items: cent…

1.5PTA集练6-1~6-4,7-3,7-4(6/26)

6-1 顺序表的删除 分数 10 List Delete( List &L, ElementType minD, ElementType maxD ){int k0;for(int i0;i<L.last;i){if(L.Data[i]<maxD&&L.Data[i]>minD){L.Data[k]L.Data[i];}}L.lastk;return L; } 这个就是注意L需要是用.,而不是用->&#x…