这篇文章主要讲解了“如何使用结构型模式”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何使用结构型模式”吧!
所谓适配器模式,就是把一个类的结构转换成另外一个类的接口。使得原本由于接口不兼容而不能工作的类都能够一起工作。
在生活中比较常见的就是当我们想连接显示器的时候,vga需要转成hdmi,还有电源适配,比如可能需要220v的充电头,但是只有110v的充电头,那么就需要将220v的充电头适配成110v的充电头。
Adaptee:
需要适配的就代码,旧接口
Adapter:
将调用转发给Adaptee
的适配器类
Target:
支持的新接口
Target
public interface Target {
void output110v();
}
Adaptee
public class Adaptee {
public void output220v() {
System.out.println("输出电压");
}
}
Adapter
public class Adapter extends Adaptee implements Target {
@Override
public void output110v() {
this.output220v();
}
}
这里需要注意的是Adapter
是继承了源类而实现了目标类
Client
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
的成员变量,然后通过构造函数进行适配
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:
代理对象类
通过上面的例子,相信对大家都对代理模式有点感觉了,但是好像又不那么恰当,上面的测试类种,还是需要新建一个player,这就相当于什么,相当于我们是在手机上登录了游戏,然后再把手机给代练者代练。而事实上,经常是把账号密码给代练即可。
这就引出了普通代理模式,客户端不能访问真实角色,只能访问代理角色,我们能够知道代理的存在。
Subject
public interface SubjectNormalGamePlayer {
public void login();
public void upgrade();
public void matches();
}
RealSubject
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
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
public class Client {
public static void main(String[] args) {
//通过测试类也很明显区别,不必要显示构造真实角色类
SubjectNormalGamePlayer proxy = new ProxySubjectNormalPlayerImpl("cutey", "123456");
proxy.login();
proxy.upgrade();
proxy.matches();
}
}
普通代理呢是去找到一个代理对象帮打,但是强制代理呢,主要是体现在“强制”,角色类会指定一个代理,其它方式找来的代理不能帮我打,一定要用我指定的代理才行。
Subject
和普通代理模式大部分代码一样,不同的是加了一个强制指定代理对象。
public interface SubjectForceGamePlayer {
//省略登录、升级和打排位等方法
//强制指定代理对象
public SubjectForceGamePlayer getForceProxy();
}
RealSubject
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
public class ProxySubjectForcePlayerImpl implements SubjectForceGamePlayer {
private SubjectForceGamePlayer gamePlayer;
//接收被代理对象的指定
public ProxySubjectForcePlayerImpl(SubjectForceGamePlayer gamePlayer) {
this.gamePlayer = gamePlayer;
}
//省略登录、升级和打比赛的方法
//没有代理对象,暂时返回自己
@Override
public SubjectForceGamePlayer getForceProxy() {
return this;
}
}
Client
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类
Subject
、RealSubject
都和普通代理模式一样
ProxySubject
我们不知道要给谁代理,所以要用到的是继承InvocationHandler类
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
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:
具体的装饰类
Component
public abstract class ComponentMobile {
//产品名字
private String name;
//省略get,set,tostring方法
public abstract void showDetails();
public abstract void onSale(String userName);
}
Concentrate Component
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
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
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();
}
}
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
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");
}
}
感谢各位的阅读,以上就是“如何使用结构型模式”的内容了,经过本文的学习后,相信大家对如何使用结构型模式这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/5078587/blog/5041203