精华内容
下载资源
问答
  • 适配器模式(Adapter Pattern)是指将一个类接口转换成客户期望另一个接口,使原本接口不兼容类可以一起工作,属于结构型设计模式。 适配器适用于以下几种业务场景: 已经存在类,它方法和需求不匹配...

    适配器模式的英文翻译是 Adapter Design Pattern。顾名思义,这个模式就是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。属于结构型设计模式。

    适配器适用于以下几种业务场景:

    • 已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况
    • 适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。有点亡羊补牢的感觉

    生活中也有非常多的应用场景,例如电源插转换头、手机充电转换头、显示器转接头。在中国民用电都是 220V 交流电,但我们手机使用的锂电池使用的 5V 直流电。因此,我们给手机充电时就需要使用电源适配器来进行转换。

    1.代码示例

    下面我们有代码来还原上面说的这个生活场景,

    创建 AC220 类,表示 220V 交流电:

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

    创建 DC5 接口,表示 5V 直流电的标

    public interface DC5 {
        int outoupDC5V();
    }
    

    创建电源适配器 PowerAdapter 类

    public class PowerAdapter implements DC5 {
    	// 组合目标类
       private AC220 ac220;
    	
        public PowerAdapter(AC220 ac220) {
            this.ac220 = ac220;
        }
    	
        public int outoupDC5V() {
        	// 调用被适配类的方法
            int adapterInput = ac220.outputAC220V();
            int adapterOutput = adapterInput / 44;
            System.out.println("使用PowerAdapter输入AC:" + adapterInput + "V,输出DC:" + adapterOutput + "V");
            return adapterOutput;
        }
    }
    

    客户端测试代码:

    public class PowerAdapterTest {
        public static void main(String[] args) {
        	// 多态
            DC5 dc5 = new PowerAdapter(new AC220());
            dc5.outoupDC5V();
        }
    }
    

    上面的案例中,通过增加 PowerAdapter 电源适配器,实现了二者的兼容…

    2.源码应用

    适配器模式在源码中的体现

    Spring 中适配器模式也应用得非常广泛,例如:SpringAOP 中的 AdvisorAdapter 类, 它有三个实现类 MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter 和 ThrowsAdviceAdapter。

    先来看顶层接口 AdvisorAdapter 的源代码:

    public interface AdvisorAdapter {
    	boolean supportsAdvice(Advice var1);
    	MethodInterceptor getInterceptor(Advisor var1);
    }
    

    再看 MethodBeforeAdviceAdapter 类:

    class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
        MethodBeforeAdviceAdapter() {
        }
        public boolean supportsAdvice(Advice advice) {
        	return advice instanceof MethodBeforeAdvice;
        }
        public MethodInterceptor getInterceptor(Advisor advisor) {
            MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
            return new MethodBeforeAdviceInterceptor(advice);
        }
    }
    

    其它两个类我这里就不把代码贴出来了。Spring 会根据不同的 AOP 配置来确定应的 Advice,跟策略模式不同的一个方法可以同时拥有多个 Advice。

    3.总结

    适配器模式是用来做适配,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。

    PS:适配器模式有两种实现方式:类适配器和对象适配器。类适配器使用继承关系来实现,对象适配器使用组合关系来实现(上面的示例)。

    一般来说,适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷。应用这种模式算是“无奈之举”,如果在设计初期,我们就能协调规避接口不兼容的问题,那这种模式就没有应用的机会了。

    优点:

    • 能提高类的透明性和复用,现有的类复用但不需要改变。
    • 目标类和适配器类解耦,提高程序的扩展性。
    • 在很多业务场景中符合开闭原则。

    缺点:

    • 适配器编写过程需要全面考虑,可能会增加系统的复杂性。
    • 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。

    4.实际应用场景示例

    很早以前开发的老系统应该都有登录接口,但是随着业务的发展和社会的进步,单纯地依赖用户名密码登录显然不能满足用户需求了。现在,我们大部分系统都已经支持多种登录方式,如 QQ 登录、微信登录、手机登录、微博登录等等,同时保留用户名密码的登录方式。虽然登录形式丰富了,但是登录后的处理逻辑可以不必改,同样是将登录状态保存到 session,遵循开闭原则。

    老系统

    假设老系统的登录逻辑 SiginService

    public class SiginService {
    
        /**
         * 注册方法
         * @param username
         * @param password
         * @return
         */
        public ResultMsg regist(String username, String password){
            return  new ResultMsg(200,"注册成功",new Member());
        }
    
    
        /**
         * 登录的方法
         * @param username
         * @param password
         * @return
         */
        public ResultMsg login(String username,String password){
            return null;
        }
    
    }
    

    为了遵循开闭原则,老系统的代码我们不会去修改。创建一个新的类继承原来的逻辑,运行非常稳定的代码我们不去改动:

    public class SinginForThirdService extends SiginService {
    
        public ResultMsg loginForQQ(String openId){
            // 1、openId是全局唯一,我们可以把它当做是一个用户名(加长)
            // 2、密码默认为QQ_EMPTY
            // 3、注册(在原有系统里面创建一个用户)
    
            // 4、调用原来的登录方法
    
            return loginForRegist(openId,null);
        }
    
        public ResultMsg loginForWechat(String openId){
            return null;
        }
    
        public ResultMsg loginForToken(String token){
            // 通过token拿到用户信息,然后再重新登陆了一次
            return  null;
        }
    
        public ResultMsg loginForTelphone(String telphone,String code){
    
            return null;
        }
    
        public ResultMsg loginForRegist(String username,String password){
            super.regist(username,null);
            return super.login(username,null);
        }
    }
    

    客户端测试代码:

    public class SiginForThirdServiceTest {
        public static void main(String[] args) {
            SinginForThirdService service = new SinginForThirdService();
            service.login("Jack","123456");
            // 不改变原来的代码,也要能够兼容新的需求
    	    // 还可以再加一层策略模式
            service.loginForQQ("abcdefgh");
            service.loginForWechat("sdfasfsa");
        }
    }
    

    通过这么一个简单的适配,完成了代码兼容。当然,我们代码还可以更加优雅,根据不同的登录方式,创建不同的 Adapter。

    适配器模式改造

    首先,创建 LoginAdapter 接口:

    /**
     * 在适配器里面,这个接口是可有可无,不要跟模板模式混淆
     * 模板模式一定是抽象类,而这里仅仅只是一个接口
     */
    public interface LoginAdapter {
        boolean support(Object adapter);
        ResultMsg login(String id, Object adapter);
    
    }
    

    分别实现不同的登录适配,QQ 登录 LoginForQQAdapter:

    public class LoginForQQAdapter implements LoginAdapter {
        public boolean support(Object adapter) {
            return adapter instanceof LoginForQQAdapter;
        }
    
        public ResultMsg login(String id, Object adapter) {
            return null;
        }
    }
    

    新浪微博登录 LoginForSinaAdapter:

    public class LoginForSinaAdapter implements LoginAdapter {
        public boolean support(Object adapter) {
            return adapter instanceof LoginForSinaAdapter;
        }
        public ResultMsg login(String id, Object adapter) {
            return null;
        }
    }
    

    手机号登录 LoginForTelAdapter:

    public class LoginForTelAdapter implements LoginAdapter {
        public boolean support(Object adapter) {
            return adapter instanceof LoginForTelAdapter;
        }
        public ResultMsg login(String id, Object adapter) {
            return null;
        }
    }
    

    Token 自动登录 LoginForTokenAdapter:

    public class LoginForTokenAdapter implements LoginAdapter {
        public boolean support(Object adapter) {
            return adapter instanceof LoginForTokenAdapter;
        }
        public ResultMsg login(String id, Object adapter) {
            return null;
        }
    }
    

    微信登录 LoginForWechatAdapter:

    public class LoginForWechatAdapter implements LoginAdapter {
        public boolean support(Object adapter) {
            return adapter instanceof LoginForWechatAdapter;
        }
        public ResultMsg login(String id, Object adapter) {
            return null;
        }
    }
    

    然后,创建第三方登录兼容接口 IPassportForThird

    /**
     * 只扩展
     * Created by Tom on 2019/3/16.
     */
    public interface IPassportForThird {
    
        /**
         * QQ登录
         * @param id
         * @return
         */
        ResultMsg loginForQQ(String id);
    
        /**
         * 微信登录
         * @param id
         * @return
         */
        ResultMsg loginForWechat(String id);
    
        /**
         * 记住登录状态后自动登录
         * @param token
         * @return
         */
        ResultMsg loginForToken(String token);
    
        /**
         * 手机号登录
         * @param telphone
         * @param code
         * @return
         */
        ResultMsg loginForTelphone(String telphone, String code);
    
        /**
         * 注册后自动登录
         * @param username
         * @param passport
         * @return
         */
        ResultMsg loginForRegist(String username, String passport);
    }
    

    实现兼容 PassportForThirdAdapter:

    public class PassportForThirdAdapter extends SiginService implements IPassportForThird {
    
        public ResultMsg loginForQQ(String id) {
    //        return processLogin(id,RegistForQQAdapter.class);
            return processLogin(id, LoginForQQAdapter.class);
        }
    
        public ResultMsg loginForWechat(String id) {
            return processLogin(id, LoginForWechatAdapter.class);
        }
    
        public ResultMsg loginForToken(String token) {
            return processLogin(token, LoginForTokenAdapter.class);
        }
    
        public ResultMsg loginForTelphone(String telphone, String code) {
            return processLogin(telphone, LoginForTelAdapter.class);
        }
    
        public ResultMsg loginForRegist(String username, String passport) {
            super.regist(username,passport);
            return super.login(username,passport);
        }
    
        private ResultMsg processLogin(String key,Class<? extends LoginAdapter> clazz){
            try{
                // 适配器不一定要实现接口
                LoginAdapter adapter = clazz.newInstance();
    
                // 判断传过来的适配器是否能处理指定的逻辑
                if(adapter.support(adapter)){
                    return adapter.login(key,adapter);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
        // 类图的快捷键  Ctrl + Alt + Shift + U
    }
    

    客户端测试代码

    public class PassportTest {
    
        public static void main(String[] args) {
    
            IPassportForThird passportForThird = new PassportForThirdAdapter();
            passportForThird.loginForQQ("");
    
        }
    }
    

    最后,来看一下类图:

    至此,我们在遵循开闭原则的前提下,实现了一个兼容多平台登录的业务场景。

    当然,目前的这个设计也并不完美,仅供参考,感兴趣的小伙伴可以继续完善这段代码。例如适配器中的参数目前是写死为 String,改为 Object[] 应该更合理。

    展开全文
  • 这种类型的设计模式属于结构型模式,它是作为现有一个包装。这种模式创建了一个装饰类,用来包装原有类,并在保持类方法签名完整性前提下,提供了额外功能。设计原则要使用装饰者模式,需要满足以下设计...

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

    这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

    设计原则

    要使用装饰者模式,需要满足以下设计原则: 
    1、多用组合,少用继承 
    2、开放-关闭原则:类应该对拓展开放,对修改关闭

    UML类图

    由上自下: 
    1、Component是基类。通常是一个抽象类或者一个接口,定义了属性或者方法,方法的实现可以由子类实现或者自己实现。通常不会直接使用该类,而是通过继承该类来实现特定的功能,它约束了整个继承树的行为。比如说,如果Component代表人,即使通过装饰也不会使人变成别的动物。 
    2、ConcreteComponent是Component的子类,实现了相应的方法,它充当了“被装饰者”的角色。 
    3、Decorator也是Component的子类,它是装饰者共同实现的抽象类(也可以是接口)。比如说,Decorator代表衣服这一类装饰者,那么它的子类应该是T恤、裙子这样的具体的装饰者。 

    4、ConcreteDecorator是Decorator的子类,是具体的装饰者,由于它同时也是Component的子类,因此它能方便地拓展Component的状态(比如添加新的方法)。每个装饰者都应该有一个实例变量用以保存某个Component的引用,这也是利用了组合的特性。在持有Component的引用后,由于其自身也是Component的子类,那么,相当于ConcreteDecorator包裹了Component,不但有Component的特性,同时自身也可以有别的特性,也就是所谓的装饰

    public interface ICar {
        void move();
    }
    
    class Car implements ICar {
        @Override
        public void move() {
            System.out.println("陆地上跑!");
        }
    }
    //Decorator装饰角色
    class SuperCar implements ICar {
        protected ICar car;
        public SuperCar(ICar car) {
            super();
            this.car = car;
        }
    
        @Override
        public void move() {
            car.move();
        }
    }
    //ConcreteDecorator具体装饰角色
    class FlyCar extends SuperCar {
    
        public FlyCar(ICar car) {
            super(car);
        }
    
        public void fly(){
            System.out.println("天上飞!");
        }
    
        @Override
        public void move() {
            super.move();
            fly();
        }
    
    }
    class WaterCar extends SuperCar {
    
        public WaterCar(ICar car) {
            super(car);
        }
        public void swim(){
            System.out.println("水上游!");
        }
        @Override
        public void move() {
            super.move();
            swim();
        }
    }
    //ConcreteDecorator具体装饰角色
    class AICar extends SuperCar {
    
        public AICar(ICar car) {
            super(car);
        }
        public void autoMove(){
            System.out.println("自动跑!");
        }
        @Override
        public void move() {
            super.move();
            autoMove();
        }
    }
    public class Client {
        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(car);
            waterCar.move();
    
            System.out.println("增加两个新的功能,飞行,水里游-------");
            WaterCar waterCar2 = new WaterCar(new FlyCar(car));
            waterCar2.move();
    
        }
    }
    
    陆地上跑!
    增加新的功能,飞行----------
    陆地上跑!
    天上飞!
    增加新的功能,水里游---------
    陆地上跑!
    水上游!
    增加两个新的功能,飞行,水里游-------
    陆地上跑!
    天上飞!
    水上游!
    展开全文
  • 装饰者模式(Decorator Pattern)是指在不改变原有对象基础之上,将功能附加到对象上,提供了比继承更有弹性替代方案(扩展原有对象功能),属于结构型模式。 装饰者模式在我们生活中应用也比较多如给煎饼加...

    装饰者模式(Decorator Pattern)是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。

    装饰者模式在我们生活中应用也比较多如给煎饼加鸡蛋;给蛋糕加上一些水果;给房子装修等,为对象扩展一些额外的职责。

    装饰者在代码程序中适用于以下场景:

    • 用于扩展一个类的功能或给一个类添加附加职责
    • 动态的给一个对象添加功能,这些功能可以再动态的撤销

    1.代码示例

    来看一个这样的场景,上班族白领其实大多有睡懒觉的习惯,每天早上上班都是踩点,于是很多小伙伴为了多赖一会儿床都不吃早餐。那么,也有些小伙伴可能在上班路上碰到卖煎饼的路边摊,都会顺带一个到公司茶水间吃早餐。卖煎饼的大姐可以给你的煎饼加鸡蛋,也可以加香肠。

    下面我们用代码还原一下码农的生活,首先创建一个煎饼 Battercake 类

    public class Battercake {
    
        protected String getMsg(){
            return "煎饼";
        }
    
        public int getPrice(){
            return 5;
        }
    }
    

    通过继承的方式扩展功能

    创建一个加鸡蛋的煎饼 BattercakeWithEgg 类:

    public class BattercakeWithEgg extends Battercake{
        @Override
        protected String getMsg() {
            return super.getMsg() + "+1个鸡蛋";
        }
    
        @Override
        // 加一个鸡蛋加1块钱
        public int getPrice() {
            return super.getPrice() + 1;
        }
    }
    

    再创建一个既加鸡蛋又加香肠的 BattercakeWithEggAndSausage 类:

    public class BattercakeWithEggAndSausage extends BattercakeWithEgg{
        @Override
        protected String getMsg() {
            return super.getMsg() + "+1根香肠";
        }
    
        @Override
        // 加一个香肠加2块钱
        public int getPrice() {
            return super.getPrice() + 2;
        }
    }
    

    编写客户端测试代码:

    public class BattercakeTest {
        public static void main(String[] args) {
    
            Battercake battercake = new Battercake();
            System.out.println(battercake.getMsg() + ",总价格:" + battercake.getPrice());
    
            Battercake battercakeWithEgg = new BattercakeWithEgg();
            System.out.println(battercakeWithEgg.getMsg() + ",总价格:" + battercakeWithEgg.getPrice());
    
            Battercake battercakeWithEggAndSausage = new BattercakeWithEggAndSausage();
            System.out.println(battercakeWithEggAndSausage.getMsg() + ",总价格:" + battercakeWithEggAndSausage.getPrice());
        }
    
    }
    

    结果如下:

    在这里插入图片描述

    运行结果没有问题。但是,如果用户需要一个加 2 个鸡蛋加 1 根香肠的煎饼,那么用我们现在的类结构是创建不出来的,也无法自动计算出价格,除非再创建一个类做定制。

    如果需求再变,一直加定制显然是不科学的。那么下面我们就用装饰者模式来解决上面的问题。

    装饰器模式重构

    首先创建一个建煎饼的抽象 Battercake 类:

    public class BaseBattercake extends Battercake {
        protected String getMsg(){
            return "煎饼";
        }
    
        public int getPrice(){
            return 5;
        }
    }
    

    创建一个基本的煎饼(或者叫基础套餐)BaseBattercake

    public class BaseBattercake extends Battercake {
        protected String getMsg(){
            return "煎饼";
        }
    
        public int getPrice(){
            return 5;
        }
    }
    

    然后,再创建一个扩展套餐的抽象装饰者 BattercakeDecotator 类:

    public abstract class BattercakeDecorator extends Battercake {
    
        // 静态代理,委派
        private Battercake battercake;
    
        public BattercakeDecorator(Battercake battercake) {
            this.battercake = battercake;
        }
    
        protected abstract void doSomething();
    
        @Override
        protected String getMsg() {
            return this.battercake.getMsg();
        }
    
        @Override
        protected int getPrice() {
            return this.battercake.getPrice();
        }
    }
    

    然后,创建鸡蛋装饰者 EggDecorator 类

    public class EggDecorator extends BattercakeDecorator {
        public EggDecorator(Battercake battercake) {
            super(battercake);
        }
    
        protected void doSomething() {
    
        }
    
        @Override
        protected String getMsg() {
            return super.getMsg() + "+1个鸡蛋";
        }
    
        @Override
        protected int getPrice() {
            return super.getPrice() + 1;
        }
    }
    

    创建香肠装饰者 SausageDecorator 类

    public class SausageDecorator extends BattercakeDecorator {
        public SausageDecorator(Battercake battercake) {
            super(battercake);
        }
    
        protected void doSomething() {
    
        }
    
        @Override
        protected String getMsg() {
            return super.getMsg() + "+1根香肠";
        }
    
        @Override
        protected int getPrice() {
            return super.getPrice() + 2;
        }
    }
    

    编写客户端测试代码

    public class BattercakeTest {
        public static void main(String[] args) {
    
            Battercake battercake;
            // 路边摊买一个煎饼
            battercake = new BaseBattercake();
            // 煎饼有点小,想再加一个鸡蛋
            battercake = new EggDecorator(battercake);
            // 再加一个鸡蛋
    	   // battercake = new EggDecorator(battercake);
            // 很饿,再加根香肠
            battercake = new SausageDecorator(battercake);
            battercake = new SausageDecorator(battercake);
            battercake = new SausageDecorator(battercake);
            battercake = new SausageDecorator(battercake);
            battercake = new SausageDecorator(battercake);
    
    
            // 跟静态代理最大区别就是职责不同
            // 静态代理不一定要满足is-a的关系
            // 静态代理会做功能增强,同一个职责变得不一样
    
            // 装饰器更多考虑是扩展
    
            System.out.println(battercake.getMsg() + ",总价:" + battercake.getPrice());
        }
    }
    

    运行结果如下:

    在这里插入图片描述

    来看一下类图

    2.源码应用

    装饰器模式在源码中也应用得非常多,在 JDK 中体现最明显的类就是 IO 相关的类,如 BufferedReader、InputStream、OutputStream,看一下常用的 InputStream 的类结构图

    再来看一个 MVC 中的 装饰者模式 HttpHeadResponseDecorator 类:

    public class HttpHeadResponseDecorator extends ServerHttpResponseDecorator {
        public HttpHeadResponseDecorator(ServerHttpResponse delegate) {
        	super(delegate);
        }
        ...
    }
    

    最后,看看 MyBatis 中的一段处理缓存的设计 org.apache.ibatis.cache.Cache 类,找到它的包定位:

    从名字上来看其实更容易理解了。比如 FifoCache 先入先出算法的缓存;LruCache 最近 最少使用的缓存;TransactionlCache 事务相关的缓存,都是采用装饰者模式。

    3.总结

    装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。除此之外,装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这个应用场景,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口。

    装饰者模式的优缺点

    优点:

    • 装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用
    • 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果
    • 装饰者完全遵守开闭原则

    缺点:

    • 会出现更多的代码,更多的类,增加程序复杂性
    • 动态装饰时,多层装饰时会更复杂

    装饰者模式和适配器模式对比

    装饰者和适配器模式都是包装模式(Wrapper Pattern),装饰者也是一种特殊的代理模式

    装饰者模式 适配器模式
    形式 是一种非常特别的适配器模式 没有层级关系,装饰器模式有层级关系
    定义 装饰者和被装饰者都实现同一个接口,主要目的是为了扩展之后依旧保留 OOP 关系 适配器和被适配者没有必然的联系,通常是采用继承或代理的形式进行包装
    关系 满足 is-a 的关系 满足 has-a 的关系
    功能 注重覆盖、扩展 注重兼容、转换
    设计 前置考虑 后置考虑
    展开全文
  • 属于行为性设计模式。 这里“算法”,我们可以理解为广义上“业务逻辑”,并不特指数据结构和算法中“算法”。这里算法骨架就是“模板”,包含算法骨架方法就是“模板方法”,这也是模板方法模式名字...

    模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。属于行为性设计模式。

    这里的“算法”,我们可以理解为广义上的“业务逻辑”,并不特指数据结构和算法中的“算法”。这里的算法骨架就是“模板”,包含算法骨架的方法就是“模板方法”,这也是模板方法模式名字的由来。

    模板方法适用于以下应用场景

    • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现
    • 各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复

    为了加深理解,下面我们来结合一个常见的业务场景。

    1.代码示例

    利用模板模式重构 JDBC 操作业务场景…

    创建一个模板类 JdbcTemplate,封装所有的 JDBC 操作。以查询为例,每次查询的表不同,返回的数据结构也就不一样。我们针对不同的数据,都要封装成不同的实体对象。 而每个实体封装的逻辑都是不一样的,但封装前和封装后的处理流程是不变的,因此, 我们可以使用模板方法模式来设计这样的业务场景。

    先创建约束 ORM 逻辑的接口 RowMapper

    /**
     * ORM映射定制化的接口
     */
    public interface RowMapper<T> {
        T mapRow(ResultSet rs, int rowNum) throws Exception;
    }
    

    再创建封装了所有处理流程的抽象类 JdbcTemplate:

    public abstract class JdbcTemplate {
        private DataSource dataSource;
    
        public JdbcTemplate(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    	
        public List<?> executeQuery(String sql, RowMapper<?> rowMapper, Object[] values){
            try {
                // 1、获取连接
                Connection conn = this.getConnection();
                // 2、创建语句集
                PreparedStatement pstm = this.createPrepareStatement(conn,sql);
                // 3、执行语句集
                ResultSet rs = this.executeQuery(pstm,values);
                // 4、处理结果集
                List<?> result = this.paresResultSet(rs,rowMapper);
                // 5、关闭结果集
                this.closeResultSet(rs);
                // 6、关闭语句集
                this.closeStatement(pstm);
                // 7、关闭连接
                this.closeConnection(conn);
                return result;
            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    
        protected void closeConnection(Connection conn) throws Exception {
            // 数据库连接池,我们不是关闭
            conn.close();
        }
    
        protected void closeStatement(PreparedStatement pstm) throws Exception {
            pstm.close();
        }
    
        protected void closeResultSet(ResultSet rs) throws Exception {
            rs.close();
        }
    
        protected List<?> paresResultSet(ResultSet rs, RowMapper<?> rowMapper) throws Exception {
            List<Object> result = new ArrayList<Object>();
            int rowNum = 1;
            while (rs.next()){
                result.add(rowMapper.mapRow(rs,rowNum ++));
            }
            return result;
        }
    
        protected ResultSet executeQuery(PreparedStatement pstm, Object[] values) throws Exception {
            for (int i = 0; i < values.length; i++) {
                pstm.setObject(i,values[i]);
            }
            return pstm.executeQuery();
        }
    
        protected PreparedStatement createPrepareStatement(Connection conn, String sql) throws Exception {
            return conn.prepareStatement(sql);
        }
    
        public Connection getConnection() throws Exception {
            return this.dataSource.getConnection();
        }
    }
    

    创建实体对象 Member 类:

    public class Member {
    
        private String username;
        private String password;
        private String nickname;
        private int age;
        private String addr;
    
        // getter/setter...
    }
    

    创建数据库操作类 MemberDao:

    public class MemberDao extends JdbcTemplate {
        public MemberDao(DataSource dataSource) {
            super(dataSource);
        }
    
        public List<?> selectAll(){
            String sql = "select * from t_member";
            // 调用模板方法 executeQuery
            // 入参自定义 RowMapper
            return super.executeQuery(sql, new RowMapper<Member>() {
                public Member mapRow(ResultSet rs, int rowNum) throws Exception {
                    Member member = new Member();
                    // 注:字段过多,可以采用原型模式
                    member.setUsername(rs.getString("username"));
                    member.setPassword(rs.getString("password"));
                    member.setAge(rs.getInt("age"));
                    member.setAddr(rs.getString("addr"));
                    return member;
                }
            },null);
        }
    }
    

    客户端测试代码:

    public class MemberDaoTest {
    
        public static void main(String[] args) {
            MemberDao memberDao = new MemberDao(null);
            List<?> result = memberDao.selectAll();
            System.out.println(result);
        }
    }
    

    2.源码应用

    先来看 JDK 中的 AbstractList,来看代码

    package java.util;
    public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
        ...
        // abstract
        abstract public E get(int index);
        ...
    }
    

    我们看到 get() 是一个抽象方法,那么它的逻辑就是交给子类来实现,我们大家所熟知的 ArrayList 就是 AbstractList 的子类 。 同理,有 AbstractList 就 有 AbstractSet 和 AbstractMap,有兴趣的小伙伴可以去看看这些的源码实现。

    还有,对于 Java Web 项目开发来说,常用的开发框架是 SpringMVC。利用它,我们只需要关注业务代码的编写,底层的原理几乎不会涉及。但是,如果我们抛开这些高级框架来开发 Web 项目,必然会用到 Servlet。实际上,使用比较底层的 Servlet 来开发 Web 项目也不难。我们只需要定义一个继承 HttpServlet 的类,并且重写其中的 doGet() 或 doPost() 方法,来分别处理 get 和 post 请求。

    在这里插入图片描述
    从上面的代码中我们可以看出,HttpServlet 的 service() 方法就是一个模板方法,它实现了整个 HTTP 请求的执行流程,doGet()、doPost() 是模板中可以由子类来定制的部分。实际上,这就相当于 Servlet 框架提供了一个扩展点(doGet()、doPost() 方法),让框架用户在不用修改 Servlet 框架源码的情况下,将业务代码通过扩展点镶嵌到框架中执行。

    3.总结

    模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。这里的“算法”,我们可以理解为广义上的“业务逻辑”,并不特指数据结构和算法中的“算法”。这里的算法骨架就是“模板”,包含算法骨架的方法就是“模板方法”,这也是模板方法模式名字的由来。

    在模板模式经典的实现中,模板方法定义为 final,可以避免被子类重写。需要子类重写的方法定义为 abstract,可以强迫子类去实现。不过,在实际项目开发中,模板模式的实现比较灵活,以上两点都不是必须的。

    模板模式有两大作用:复用和扩展。其中,复用指的是,所有的子类可以复用父类中提供的模板方法的代码。扩展指的是,框架通过模板模式提供功能扩展点,让框架用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能。

    优点:

    • 利用模板方法将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性
    • 将不同的代码不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性
    • 把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台, 符合开闭原则。

    缺点:

    • 类数目的增加,每一个抽象类都需要一个子类来实现,这样导致类的个数增加
    • 类数量的增加,间接地增加了系统实现的复杂度
    • 继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍
    展开全文
  • 设计模式

    2021-03-25 09:58:56
    设计模式 GoF 在《设计模式》 一种归纳了23种设计模式,而他们又属于三种类型模式,分别是 创建者模式、结构型模式 和 行为型模式。 这23种设计模式主要是基于以下的对象设计原则...创建型设计模式主要解决“对象
  • 这种类型的设计模式属于创建模式,它提供了一种创建对象最佳方式。 这种模式涉及到一个单一类,该类负责创建自己对象,同时确保只有单个对象被创建。 这个类提供了一种访问其唯一对象方式,可以直接...
  • 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品; 系统一次只可能消费其中某一族产品,即同族的产品一起使用。 优缺点: 优点: 抽象工厂模式除了具有工厂方法模式的特点外,其他主要优点如下...
  • 本文转自 设计模式之桥梁模式和策略模式的区别 - xingjiarong的专栏 - 博客...桥接(Bridge)模式是结构型模式的一种,而策略(strategy)模式则属于行为模式。以下是它们的UML结构图。 桥梁模式:    策略模式: 
  • 设计模式之装饰模式

    2019-08-14 13:40:14
    这种类型的设计模式属于结构型模式(结构型模式设计,它关注类和对象组合。继承这一概念被用来组合接口和定义组合对象获得新功能方式。它包括以下几种具体的设计模式:适配器模式、桥接模式、过滤器模式、组合...
  • 认识设计模式

    2021-03-10 16:26:34
    设计模式是一套被反复使用、多数人知晓、经过分类编目、代码设计经验总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序重用性。 分为: 创建型模式 结构型模式 行为型...
  • 这种类型的设计模式属于创建模式,它提供了一种创建对象最佳方式。 这种模式涉及到一个单一类,该类负责创建自己对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象方式,可以直接访问...
  • JAVA设计模式

    2018-04-18 09:43:58
    设计模式主要可以分为三大类:创建模式、...行为模式更关注对象的的行为与对象之间的通信,如策略模式,状态模式等,以下是我对这些设计模式的简单理解。 1.工厂模式 工厂模式属于创建模式,有简单工厂、工厂...
  • 将一个类接口转换成客户期望另一个接口,使 原本接口不兼容类可以一起工作,属于结构型设计模式。 适配器适用于以下几种业务场景: 1、已经存在类,它方法和需求不匹配(方法结果相同或相似)情况...
  • 适配器模式是指将一个类接口转换为用户期望另一个接口,使原本接口不兼容类可以一起工作,属于结构型设计模式。 适配器模式适用于以下几种业务场景: 1.已经存在方法和需求不匹配(方法相同或相似)...
  • 设计模式-适配器模式

    2019-03-19 17:40:17
    适配器模式(Adapter Pattern)是指将一个类接口转换成客户期望另一个接口,使原本接口不兼容类可以一起工作,属于结构型设计模式。 适配器适用于以下几种业务场景: 1、已经存在类,它方法和需求不匹配...
  • 适配器模式是指将一个类接口转换成用户期望另一个接口,使原本接口不兼容类可以一起工作,属于结构型设计模式。 1.2 适用场景 适配器模式适用于以下场景: 已经存在方法和需求不匹配; 不是软件...
  • 设计模式之代理模式

    2020-08-05 23:50:52
    代理模式属于结构型模式,是23种设计模式中较为重要的一种,代理模式分为静态代理和动态代理,动态代理又分为基于接口实现的动态代理和基于子类实现的动态代理;在jdk源码中,很多底层的实现也是基于代理模式的,...
  • 适配器模式(Adapter Pattern)是指将一个类接口转换成用户期望另一个接口,使原本接口不兼容类可以一起工作,属于结构型设计模式。适配器模式适用于以下集中业务场景: 已经存在方法和需求不匹配...
  • 单例设计模式

    2021-03-11 18:40:23
    这种类型的设计模式属于创建模式,它提供了一种创建对象最佳方式。 这种模式涉及到一个单一类,该类负责创建自己对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象方式,可以直接访问...
  • 桥接(Bridge)模式是结构型模式的一种,而策略(strategy)模式则属于行为模式。以下是它们的UML结构图。 在桥接模式中,Abstraction通过聚合的方式引用Implementor。   在策略模式中,Contex
  • 这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。 二、模式的结构 过滤器模式(Filter Pattern)包含以下主要角色: 1.AbstractFilter(抽象过滤器角色):在客户端可以调用它的方法,在抽象过滤...
  • 这种类型的设计模式属于创建模式,它提供了一种创建对象最佳方式。 这种设计模式涉及到一个单一类,该类负责创建自己对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象方式。可以直接...
  • 这种类型的设计模式属于结构型模式,它创建了对象组树形结构(Copy)。 理解:存在前提条件,只有满足以下两个条件,才建议使用组合模式(Composite Pattern) 一组相似对象,说明对象相似度...
  • 桥接(Bridge)模式是结构型模式的一种,而策略(strategy)模式则属于行为模式。以下是它们的UML结构图。 在桥接模式中,Abstraction通过聚合的方式引用Implementor。   在策略模式中,Context也使用聚合的方式...

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 188
精华内容 75
关键字:

以下属于结构型设计模式的是