前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >每天5分钟-结构型模式(一)

每天5分钟-结构型模式(一)

作者头像
用户8902830
发布2021-08-12 11:01:57
1810
发布2021-08-12 11:01:57
举报
文章被收录于专栏:CodeNoneCodeNone

适配器模式

所谓适配器模式,就是把一个类的结构转换成另外一个类的接口。使得原本由于接口不兼容而不能工作的类都能够一起工作。

在生活中比较常见的就是当我们想连接显示器的时候,vga需要转成hdmi,还有电源适配,比如可能需要220v的充电头,但是只有110v的充电头,那么就需要将220v的充电头适配成110v的充电头。

Adaptee: 需要适配的就代码,旧接口 Adapter: 将调用转发给Adaptee 的适配器类 Target: 支持的新接口

适配器模式UML

类适配器模式

Target

代码语言:javascript
复制
public interface Target {
    void output110v();
}

Adaptee

代码语言:javascript
复制
public class Adaptee {
    public void output220v() {
        System.out.println("输出电压");
    }
}

Adapter

代码语言:javascript
复制
public class Adapter extends Adaptee implements Target {
    @Override
    public void output110v() {
        this.output220v();
    }
}

这里需要注意的是Adapter继承了源类而实现了目标类

Client

代码语言:javascript
复制
public class Client {
    public static void main(String[] args) {
        Target target = new Adapter();
        target.output110v();
    }
}

虽然我们是使用了output110v的充电头,但是经过Adapter后,最终通过this.output220v() 会调用output220v的充电头,这就是把output220v适配成了output110v

对象适配器模式

其实说得这么高大上,其实就是Adaptor 的实现方式不同,类适配器模式采用继承了源类(也就是需要适配的类)实现了目标类。

这样就存在一个问题,当我们需要适配多个类的时候就会出现问题,因为java中是允许实现多个接口,但是只能继承一个类

为了解决这个问题,我们可以把需要适配的类作为Adapter 的成员变量,然后通过构造函数进行适配

代码语言:javascript
复制
public class NewAdapter implements Target{
    Adaptee adaptee;

    public NewAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void output110v() {
        adaptee.output220v();
    }
}

代理模式


代理模式也算是比较常用的设计模式之一,大家接触最多的spring aop,就是采用动态代理模式来完成的。代理模式可以分为普通代理模式,强制代理模式,动态代理模式,也是本文着重讲解的,当然还有其代理模式。

先来用一段代码来体会代理模式,场景是玩家打游戏。

Subject: 共同接口,客户端使用的现有接口 RealSubject: 真实对象的类 ProxySubject: 代理对象类

代理模式UML

普通代理模式


通过上面的例子,相信对大家都对代理模式有点感觉了,但是好像又不那么恰当,上面的测试类种,还是需要新建一个player,这就相当于什么,相当于我们是在手机上登录了游戏,然后再把手机给代练者代练。而事实上,经常是把账号密码给代练即可。

这就引出了普通代理模式,客户端不能访问真实角色,只能访问代理角色,我们能够知道代理的存在。

Subject

代码语言:javascript
复制
public interface SubjectNormalGamePlayer {
    public void login();
    public void upgrade();
    public void matches();
}

RealSubject

代码语言:javascript
复制
public class RealSubjectNormalPlayerImpl implements SubjectNormalGamePlayer {
    private String name;
    private String password;

    public RealSubjectNormalPlayerImpl(String name, String password) {
        this.name = name;
        this.password = password;
    }

    @Override
    public void login() {
        if(name.equals("cutey") && password.equals("123456")) {
            System.out.println(name + "登录成功");
        }
    }

    @Override
    public void upgrade() {  
        System.out.println(name + "升级");
    }

    @Override
    public void matches() {
        System.out.println(name + "打排位赛");
    }
}

ProxySubject

代码语言:javascript
复制
public class ProxySubjectNormalPlayerImpl implements SubjectNormalGamePlayer {
    SubjectNormalGamePlayer gamePlayer;

    //注意这里的区别,我们是拿账号和密码去登录真实角色
    //并不是直接拿真实角色的手机来打
    public ProxySubjectNormalPlayerImpl(String name, String password) {
        gamePlayer = new RealSubjectNormalPlayerImpl(name, password);
    }

    @Override
    public void login() {
        System.out.print("代练:");
        gamePlayer.login();
    }

    @Override
    public void upgrade() {
        System.out.print("代练:");
        gamePlayer.upgrade();
    }

    @Override
    public void matches() {
        System.out.print("代练:");
        gamePlayer.matches();
    }
}

Client

代码语言:javascript
复制
public class Client {
    public static void main(String[] args) {
        //通过测试类也很明显区别,不必要显示构造真实角色类
        SubjectNormalGamePlayer proxy = new ProxySubjectNormalPlayerImpl("cutey", "123456");
        proxy.login();
        proxy.upgrade();
        proxy.matches();
    }
}

强制代理


普通代理呢是去找到一个代理对象帮打,但是强制代理呢,主要是体现在“强制”,角色类会指定一个代理,其它方式找来的代理不能帮我打,一定要用我指定的代理才行。

Subject

和普通代理模式大部分代码一样,不同的是加了一个强制指定代理对象

代码语言:javascript
复制
public interface SubjectForceGamePlayer {
    //省略登录、升级和打排位等方法
    
    //强制指定代理对象
    public SubjectForceGamePlayer getForceProxy();
}

RealSubject

代码语言:javascript
复制
public class ForceGamePlayerImpl implements IForceGamePlayer {
    private String name;
    private String password;

    private IForceGamePlayer proxy = null;      //指定需要谁来代理


    public ForceGamePlayerImpl(String name, String password) {
        this.name = name;
        this.password = password;
    }
    
    @Override
    public IForceGamePlayer getForceProxy() {
        //强制指定代理类,并且只有这样才能给proxy赋值
        proxy =  new ForceProxyGamePlayerImpl(this);
        return proxy;
    }

    @Override
    public void login() {
        //只要不是自己指定的proxy,其它方式proxy肯定是null
        if(proxy != null) {
            if(name.equals("imperfect") && password.equals("123456")) {
                System.out.println(name + "登录成功");
            }
        } else {
            System.out.println("需要代理");
        }

    }

    @Override
    public void upgrade() {
        if(proxy != null) {
            System.out.println(name + "升级");
        } else {
            System.out.println("需要代理");
        }

    }

    @Override
    public void matches() {
        if(proxy != null) {
            System.out.println(name + "打排位赛");
        } else {
            System.out.println("需要代理");
        }

    }
}

ProxySubject

代码语言:javascript
复制
public class ProxySubjectForcePlayerImpl implements SubjectForceGamePlayer {

    private SubjectForceGamePlayer gamePlayer;

 //接收被代理对象的指定
    public ProxySubjectForcePlayerImpl(SubjectForceGamePlayer gamePlayer) {
        this.gamePlayer = gamePlayer;
    }


    //省略登录、升级和打比赛的方法

    //没有代理对象,暂时返回自己
    @Override
    public SubjectForceGamePlayer getForceProxy() {
        return this;
    }

}

Client

代码语言:javascript
复制
public class Client {
    public static void main(String[] args) {
        wrongProxy1();
        wrongProxy2();
        correctProxy();
    }

    public static void correctProxy() {
        SubjectForceGamePlayer player = new RealSubjectForcePlayerImpl("cutey", "123456");

        //你这个代理必须是我指定的,并且强制要有
        SubjectForceGamePlayer proxy = player.getForceProxy();
        proxy.login();
        proxy.upgrade();
        proxy.matches();
    }

    public static void wrongProxy1() {
        SubjectForceGamePlayer player = new RealSubjectForcePlayerImpl("cutey", "123456");
        SubjectForceGamePlayer proxy = new ProxySubjectForcePlayerImpl(player);
        proxy.login();
        proxy.upgrade();
        proxy.matches();
    }

    public static void wrongProxy2() {
        SubjectForceGamePlayer player = new RealSubjectForcePlayerImpl("cutey", "123456");
        player.login();
        player.upgrade();
        player.matches();
    }
}

动态代理


这个应该是代理模式中用的比较多的,也是我觉得最需要各位小伙伴理解并且掌握的。所谓动态代理是指,不用在编译器指定为谁代理,而是在运行期再获得被代理的对象并且执行代理的方法。

下面将要讲的例子是利用jdk中提供的InvocationHandler和Proxy类

SubjectRealSubject 都和普通代理模式一样

ProxySubject

我们不知道要给谁代理,所以要用到的是继承InvocationHandler类

代码语言:javascript
复制
public class ProxySubjectDynamicPlayerImpl implements InvocationHandler {
    Class cls = null; //要代理的类
    Object obj = null; //需要代理的对象

    //指定需要代理的对象
    public ProxySubjectDynamicPlayerImpl(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //调用代理对象的方法
        Object result = method.invoke(this.obj, args);
        if(method.getName().equalsIgnoreCase("login")) {
            System.out.println("异地登陆提醒:有人登录我的账户");
        }
        return result;
    }
}

Client

代码语言:javascript
复制
public class Client {
    public static void main(String[] args) {
        SubjectDynamicPlayer player = new RealSubjectDynamicPlayerImpl("imperfect", "123456");

        //把需要代理的信息交给handler,还记得invoke方法吗
        //在invoke方法中已经实现了被代理对象的方法
        InvocationHandler handler = new ProxySubjectDynamicPlayerImpl(player);

        //获取被代理类的类加载属性
        ClassLoader cl = player.getClass().getClassLoader();

        //获取被代理类的接口
        Class[] interfaces = player.getClass().getInterfaces();

        //把上述的三个信息交给Proxy创建出一个代理类
        SubjectDynamicPlayer proxy = (SubjectDynamicPlayer) Proxy.newProxyInstance(cl, interfaces, handler);

        proxy.login();
        proxy.upgrade();
        proxy.matches();
    }
}

在编译时我们是完全不知道给谁代理,一切都是在运行时才知道,这就是“动态”

装饰模式

装饰模式就是动态地给一个对象添加一些恩爱的职责,就增加功能来说,装饰模式比生成子类更为灵活。

无论干什么,最重要的都是扬长避短,对于卖手机也是如此,肯定是把卖点详细地介绍,而对于缺点能不提就不提。

Component: 定义一个对象的接口,可以给这些对象动态地添加职责 ConcreteComponent: 具体的对象 Decorator: 装饰抽象类,继承了Component,从外类来扩展Component ConcentrateDecorator: 具体的装饰类

装饰模式UML

装饰模式

Component

代码语言:javascript
复制
public abstract class ComponentMobile {
    //产品名字
    private String name;

    //省略get,set,tostring方法

    public abstract void showDetails();

    public abstract void onSale(String userName);

}

Concentrate Component

代码语言:javascript
复制
public class ConcreteComponentOnePlus extends ComponentMobile {

    public ConcreteComponentOnePlus(String name) {
        super(name);
    }

    @Override
    public void showDetails() {
        System.out.println("处理器:骁龙888 \r\n拍照:哈苏专业模式 \r\n屏幕:2k+120hz 柔性屏 \r\n充电:65w快充");
    }

    @Override
    public void onSale(String userName) {
        System.out.println(userName + "购买了" + getName());
    }
}

Decorator

代码语言:javascript
复制
public abstract class Decorator extends ComponentMobile {

    //把要装饰的手机拿给我
    private ComponentMobile mobile;

    public Decorator(String name, ComponentMobile mobile) {
        super(name);
        this.mobile = mobile;
    }

 //细节还是要展示的
    //只不过怎么展示呢,子类可以加以修饰
    public void showDetails() {
        mobile.showDetails();
    }

    //手机也是要出售的
    public void onSale(String name) {
        mobile.onSale(name);
    }

}

注意,我们手机的细节还是要展示的,不能说做的不好就不说出来,欺骗消费者。能把你认出来叫化妆,不能把你认出来叫整容,我们讲的是装饰模式,不是整容模式。

Concrete Decorator

代码语言:javascript
复制
public class ConcreteDecoratorSystem extends Decorator {

    public ConcreteDecoratorSystem(String name, ComponentMobile mobile) {
        super(name, mobile);
    }

    //装饰系统
    public void decorateScreen() {
        System.out.println("出厂配备了ColorOS,其它型号的手机也会逐步适配");

    }

    @Override
    public void showDetails() {
        //想先介绍了系统,再说其他参数
        decorateScreen();
        super.showDetails();
    }
}
代码语言:javascript
复制
public class ConcreteDecoratorPrice extends Decorator {


    public ConcreteDecoratorPrice(String name, ComponentMobile mobile) {
        super(name, mobile);
    }

    //公布价格
    public void decoratePrice() {
        System.out.println("8 + 128:4999");
        System.out.println("8 + 256: 5499");
    }

    @Override
    public void showDetails() {
        super.showDetails();
        //介绍完其它的后,公布性价比较高的价格
        decoratePrice();
    }
}

Client

代码语言:javascript
复制
public class Client {
    public static void main(String[] args) {
        //手机发布会,原产品
        ComponentMobile mobile = new ConcreteComponentOnePlus("OnePlus 9 Pro");
        //装饰下系统
        mobile = new ConcreteDecoratorSystem(mobile.getName(), mobile);
        //装饰下价格
        mobile = new ConcreteDecoratorPrice(mobile.getName(), mobile);
        mobile.showDetails();

        //用户一看,诶,不错不错,买了
        mobile.onSale("cutey");
    }
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-05-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CodeNone 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 适配器模式
    • 适配器模式UML
      • 类适配器模式
        • 对象适配器模式
        • 代理模式
          • 代理模式UML
            • 普通代理模式
              • 强制代理
                • 动态代理
                • 装饰模式
                  • 装饰模式UML
                    • 装饰模式
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档