Java设计模式之结构型模式
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
一、适配器模式
什么是适配器模式?适配器模式是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。 简单的来说就是通过某个接口将不兼容的两个类进行兼容,俗称转换器。
举例,小明买了一个美国的进口电视,进口的电视需要的电压是110V,而国内的输出电压是220V,现在需要一个转换器(适配器)对国内的220V电压转成电视能用的110V电压。
/**
* 目标
*/
public interface Target {
// 转换成110V电压
public void convertTo110V();
}
/**
* 源头
*/
public class Source {
// 输入的是220V电压
public void input220V() {
System.out.println("源头是220V电压");
}
}
/**
* 转换器
*/
// (1)类适配器模式
public class ConvertAdapter extends Source implements Target {
// 把原来的220V转成目标110V
public void convertTo110V() {
input220V();
System.out.println("转换中...");
System.out.println("输出110V");
}
}
/**
* 转换器
*/
// (2)对象适配器模式
public class ConvertAdapter implements Target {
private Source mSource;
public ConvertAdapter(Source source) {
this.mSource = source;
}
// 把原来的220V转成目标110V
public void convertTo110V() {
mSource.input220V();
System.out.println("转换中...");
System.out.println("输出110V");
}
}
优点:
(1)更好的复用性,系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
(2)解耦性,将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。
缺点:
过多的使用适配器,会让系统非常零乱,不易整体进行把握。
二、装饰者模式
装饰器模式作用是针对目标方法进行增强,提供新的功能或者额外的功能。
而实际上,装饰器模式和代理模式的实现方式基本一致,只在目标的存在上有些差别,这个后面我们具体讲述。
/**
* 被装饰的目标
*/
public interface House {
void show();
}
/**
* House的实现类
*/
public class ZhangSanHouse implements House {
public void show() {
System.out.println("这是张三的房子");
}
}
/**
* House的实现类
*/
public class LiSiHouse implements House {
public void show() {
System.out.println("这是李四的房子");
}
}
/**
* 装饰者
*/
public class Decorator implements House {
private House mHouse;
public Decorator(House house) {
this.mHouse = house;
}
public void show() {
System.out.println("装饰前");
mHouse.show();
System.out.println("装饰后");
}
}
// 客户端调用如下
public static void main(String[] args) {
House house = new LiSiHouse();
Decorator decorator = new Decorator(house);
decorator.show();
}
和代理模式的区别主要是站在应用及思想角度:
(1)装饰模式正如其名,假设你买了一个手机,但是又怕它摔破,于是你在手机上贴膜和加保护套。但这个手机从核心上来说,还是你之前的手机,之前添加了一些装饰,相当于增强自己。
(2)代理模式就像明星找了一个经纪人,经纪人代理了明星的很多工作,你需要找明星的话,必须通过代理人(经纪人)。
三、代理模式
什么是代理?什么是静态代理?什么是动态代理?
举个例子:
车站(被代理)需要卖票,为了方便更多的客户,车站在很多地方找了第三方的合作机构进行卖票(这个角色就是代理),票卖出去之后,第三方机构会抽取一定的提成,同时也需要提供除了票以外的一些其他售后服务,在这里第三方机构就是代理。所谓的静态代理就是,全国各地除了车站外还要多设置100个售票的地方,而这100个地方就需要找100个第三方机构来管理和卖,这种形式就静态代理,成本较高。假设找一个第三方机构,让它一个去管理这100个地方的售票和售后服务,在不同的地方切换不同形式的售后服务,这个叫动态代理。
1、静态代理
由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
/**
* 票
*/
public interface Ticket {
public void sell();
}
/**
* 被代理者
*
*/
public class BusStation implements Ticket {
public void sell() {
System.out.println("车站里卖票");
}
}
/**
* 第三方机构代理1
*/
public class TicketProxy1 implements Ticket {
private Ticket mTicket; // 被代理者
public TicketProxy1(Ticket ticket) {
mTicket = ticket;
}
public void sell() {
System.out.println("第三方代理机构1收取服务费");
mTicket.sell();
System.out.println("第三方代理机构1提供售后服务");
}
}
/**
* 第三方机构代理2
*/
public class TicketProxy2 implements Ticket {
private Ticket mTicket; // 被代理者
public TicketProxy2(Ticket ticket) {
mTicket = ticket;
}
public void sell() {
System.out.println("第三方代理机构2收取服务费");
mTicket.sell();
System.out.println("第三方代理机构2提供售后服务");
}
}
public static void main(String[] args) {
BusStation busStation = new BusStation(); // 被代理者
TicketProxy1 proxy1 = new TicketProxy1(busStation); // 代理者1
TicketProxy2 proxy2 = new TicketProxy2(busStation); // 代理者2
proxy1.sell(); // 调用代理者1的方法
proxy2.sell(); // 调用代理者2的方法
}
2、动态代理
在程序运行时,运用反射机制动态创建而成。
/**
* 票
*/
public interface Ticket {
public void sell();
}
/**
* 被代理者,本尊
*
*/
public class BusStation implements Ticket {
public void sell() {
System.out.println("车站里卖票");
}
}
/**
* 全部代理
*/
public class AllProxy implements InvocationHandler {
private Object subject;
// 构造方法,给我们被代理的真实对象赋初值
public AllProxy(Object subject) {
this.subject = subject;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("第三方代理机构收取服务费");
// method表示的被代理对象的接口方法,invoke去调用该方法
method.invoke(subject, args);
System.out.println("第三方代理机构提供售后服务");
return null;
}
}
public static void main(String[] args) {
// 被代理者
BusStation busStation = new BusStation();
// Proxy.newProxyInstance会动态创建出一个代理对象
// 参数一,classLoader,类加载器
// 参数二,被代理者提供的接口,表示你们可以代理我了
// 参数三,InvocationHandler接口实现类,可以理解为动态创建代理对象时的回调
Ticket ticket = (Ticket) Proxy.newProxyInstance(busStation.getClass()
.getClassLoader(), busStation.getClass().getInterfaces(),
new AllProxy(busStation));
// 调用代理对象的sell方法
ticket.sell();
}
总结如下:
1、静态代理实现较简单,代理类在编译期生成,效率高。缺点是会生成大量的代理类。
2、动态代理不要求代理类和委托类实现同一个接口,但是委托类(被代理类)需要实现接口,代理类需要实现InvocationHandler接口。
3、动态代理要求代理类实现InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。
四、外观模式
外观(Facade)模式的结构比较简单,主要是定义了一个高层接口。它包含了对各个子系统的引用,客户端可以通过它访问各个子系统的功能。
public class SystemA {
public void doA() {
System.out.println("SystemA>>>do something");
}
}
public class SystemB {
public void doB() {
System.out.println("SystemB>>>do something");
}
}
public class SystemC {
public void doC() {
System.out.println("SystemC>>>do something");
}
}
/**
* 外观模式主类,对外部调用来说,直接调用该类的方法即可。
* 屏蔽了子系统的调用细节。
*/
public class Facade {
private SystemA mSystemA;
private SystemB mSystemB;
private SystemC mSystemC;
public Facade() {
mSystemA = new SystemA();
mSystemB = new SystemB();
mSystemC = new SystemC();
}
// 供外部调用的方法
public void show() {
mSystemA.doA();
mSystemB.doB();
mSystemA.doA();
mSystemC.doC();
}
}
客户端不需要知道系统内部的复杂联系,整个系统只提供一个“接待员”即可。
优点:
(1)减少了系统的相互依赖。
(2)提高了灵活性。不管系统内部如何变化,只要不影响到外观对象,任你*活动。
(3)提高了安全性。想让你访问子系统的哪些业务就开通哪些逻辑,不在外观上开通的方法,你就访问不到。
缺点:
不符合开不原则,修改很麻烦。当新增一个子系统的时候,需要修改“接待员”类。
五、享元模式
如果在一个系统中存在多个相同的对象,那么只需要共享一份对象的拷贝,而不必为每一次使用都创建新的对象。目的是提高系统性能。上面的概念乍一听好像单例模式其实不是,单例模式只保存一个对象,但是这里可以有很多个不同对象,但是每个对象只有一个实例而已。也就是说享元模式使用了单例模式。
/**
* 抽象享元类:形状
*/
public abstract class Shape {
// 绘画
public abstract void draw();
}
/**
* 实现类-圆
*/
@Data
public class Circle extends Shape {
private String color;
public Circle(String color) {
this.color = color;
}
@Override
public void draw() {
System.out.println("画了一个" + this.color + "圆");
}
}
/**
* 享元工厂类
*/
public class ShapeFactory {
// 缓存对象
private static HashMap<String, Shape> shapeMap = new HashMap();
public static Shape getShape(String color) {
Shape shape = shapeMap.get(color);
if (shape == null) {
// 没有则创建新的对象
shape = new Circle(color);
shapeMap.put(color, shape);
}
return shape;
}
public static int getSum(){
return shapeMap.size();
}
}
// 使用
public class DrawClient{
public static void main(String[] args) {
Shape shape1 = ShapeFactory.getShape("红");
shape1.draw();
Shape shape2 = ShapeFactory.getShape("灰");
shape2.draw();
Shape shape3 = ShapeFactory.getShape("绿");
shape3.draw();
Shape shape4 = ShapeFactory.getShape("灰");
shape4.draw();
System.out.println("一共绘制了"+ShapeFactory.getSum()+"种颜色的圆形"); // 3
}
}
优点:
(1)节省内存空间,对于可重复的对象只会被创建一次,对象比较多时,就会极大的节省空间。
(2)提高效率,由于创建对象的数量减少,所以对系统内存的需求也减小,使得速度更快,效率更高。
缺点:
系统的复杂度更高了。需要分离出内部状态和外部状态,如果这两个状态都需要管理就更麻烦了。
本文地址:https://blog.csdn.net/duncan891101/article/details/107851673