文章目录
- 代理模式
- 示例
- 结构
- 分类
- 动态代理
- 装饰模式
- 示例
- 结构
- 使用场景
- 与代理模式区别
- Context
- 外观模式
- 结构
- 示例
- 使用场景
- Context
- 享元模式
- 结构
- 示例
- 使用场景
- Message
代理模式
代理模式(Proxy Pattern) 是一种结构型设计模式,它提供了一个代理对象,控制对目标对象的访问。代理对象通常在客户端与目标对象之间起到中介的作用,用于扩展目标对象的功能。
定义:为其他对象提供一种代理以控制对这个对象的访问
示例
定义接口
// 公共接口
public interface IShop {void buy();
}
实现真实类
// 真实对象
public class Buyer implements IShop{@Overridepublic void buy() {System.out.println("购买");}
}
定义代理类
// 代理类
public class Buying implements IShop{private IShop iShop;public Buying(IShop iShop) {this.iShop = iShop;}@Overridepublic void buy() {iShop.buy();}
}
测试代码
public class Client {public static void main(String[] args) {// 创建真实对象IShop person1 = new Buyer();// 创建代理对象IShop proxy = new Buying(person1);// 使用代理对象proxy.buy();}
}
结构
Subject(抽象主题):定义目标对象和代理对象的公共接口。
RealSubject(真实主题):实现了Subject接口,定义了具体的业务逻辑。
Proxy(代理):代理对象,包含对真实主题的引用,并且可以在对真实主题的调用前后添加额外的功能。
分类
- 静态代理
静态代理是指在编译时就已经确定代理类的实现,通常是手动编写代理类。静态代理的关键特点是代理类与目标类之间有一一对应的关系。
- 动态代理
动态代理是在运行时动态生成代理对象,而不是在编译时就明确写好的。Java提供了通过反射机制动态的生成代理对象的机制
动态代理
java提供了java.lang.reflect.InvocationHandler
,一个便捷的动态代理接口,实现它要重写其调用方法invoke
// 目标接口
public interface Subject {void request();
}// 目标类
public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("真实主题:处理请求。");}
}// 动态代理的处理器
public class DynamicProxyHandler implements InvocationHandler {private Object target;public DynamicProxyHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代理:在真实主题处理请求之前。");Object result = method.invoke(target, args); // 调用目标方法System.out.println("代理:在真实主题处理请求之后。");return result;}
}// 测试
public class DynamicProxyDemo {public static void main(String[] args) {// 创建一个真实的目标对象RealSubject realSubject = new RealSubject();// 使用Proxy创建代理对象Subject proxy = (Subject) Proxy.newProxyInstance(// 目标类的类加载器realSubject.getClass().getClassLoader(),// 目标类实现的接口new Class[] {Subject.class}, // 代理的处理器(即动态代理的逻辑)new DynamicProxyHandler(realSubject));// 调用代理对象的方法proxy.request();}
}
装饰模式
定义:
动态地向对象添加额外的职责,而不改变其结构。
使用场景:
需要透明且动态地扩展类的功能时
示例
// 抽象组件类
public abstract class Component {// 抽象操作方法,由子类实现public abstract void operate();
}
// 具体组件类
public class ConcreteComponent extends Component {@Overridepublic void operate() {System.out.println("执行基本操作");}
}
// 装饰者基类
public abstract class Decorator extends Component {protected Component component; // 持有组件对象的引用public Decorator(Component component) {this.component = component;}@Overridepublic void operate() {component.operate(); // 调用组件的操作}
}
// 具体装饰者实现类
public class ConcreteDecoratorA extends Decorator {public LoggingDecorator(Component component) {super(component);}@Overridepublic void operate() {System.out.println("开始");super.operate(); // 调用原始操作System.out.println("结束");}
}
public class DecoratorPatternDemo {public static void main(String[] args) {// 创建具体组件Component component = new ConcreteComponent();// 根据组件对象构造装饰者componentA并调用Component componentA = new ConcreteDecoratorA(component);componentA.operate();}
}
结构
- 抽象组件(Component): 一个接口或抽象类,被装饰的原始对象
- 具体组件(ConcreteComponent): 抽象组件的具体实现,是被装饰的核心对象
- 抽象装饰者(Decorator): 抽象类或接口,持有一个组件对象的引用,并定义与组件一致的接口
- 具体装饰者(ConcreteDecorator): 抽象装饰者实现类,对抽象装饰者做出具体的实现
使用场景
- 动态扩展一个类的功能。
- 替代多层次的继承结构。
- 当不能直接修改类或不希望影响其他对象时。
与代理模式区别
装饰模式:
- 目的:装饰模式用于动态地扩展一个对象的功能,且对客户端透明。它是继承关系的替代方案,可以通过包装原对象并为其添加新的功能,而不改变原对象的结构。
- 使用场景:当你需要扩展对象的功能时,不希望直接修改原有的类时
代理模式:
- 目的:代理模式用于为其他对象提供一个替代品或代理对象,以便通过代理对象来控制对原对象的访问。代理对象本身不增强原对象的功能,而是控制访问、延迟加载、安全检查等。
- 使用场景:当你希望控制对某个对象的访问(例如,延迟加载、访问权限控制、日志记录等)时,可以使用代理模式。
装饰模式强调对对象功能的增强和扩展。
代理模式关注对对象的访问控制、管理、替代。
核心区别:装饰模式是为了增强原对象的功能,而代理模式是为了控制原对象的访问。
Context
// 抽象组件
public abstract class Context {public abstract void startActivity(@RequiresPermission Intent intent);public void startActivity(Intent intent, Bundle options);
}
// 具体组件实现类
class ContextImpl extends Context{@Overridepublic void startActivity(Intent intent) {warnIfCallingFromSystemProcess();startActivity(intent, null);}@Overridepublic void startActivity(Intent intent, Bundle options) {warnIfCallingFromSystemProcess();final int targetSdkVersion = getApplicationInfo().targetSdkVersion;if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0&& (targetSdkVersion < Build.VERSION_CODES.N|| targetSdkVersion >= Build.VERSION_CODES.P)&& (options == null|| ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {throw new AndroidRuntimeException("Calling startActivity() from outside of an Activity"+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."+ " Is this really what you want?");}mMainThread.getInstrumentation().execStartActivity(getOuterContext(), mMainThread.getApplicationThread(), null,(Activity) null, intent, -1, options);}
}
// 装饰者,持有ContextImpl的对象
public class ContextWrapper extends Context {Context mBase;@Overridepublic void startActivity(Intent intent) {mBase.startActivity(intent);}@Overridepublic void startActivity(Intent intent, @Nullable Bundle options) {mBase.startActivity(intent, options);}
}
Activity extends ContextThemeWrapper
ContextThemeWrapper extends ContextWrapper
Activity
就是具体装饰者
ContextImpl
的创建和ContextWrapper
对ContextImpl
引用在ActivityThread
的main
函数中进行
外观模式
外观模式(Facade Pattern)是一种结构型设计模式,它为复杂的子系统提供一个简单的接口。
目的是简化系统的使用方式,使得调用者可以通过一个统一的入口来访问系统中的多个子系统,而不需要关心子系统的内部实现细节。
结构
外观模式通常包含以下几个角色:
-
Facade(外观类):提供一个简化的接口,委托请求给子系统。
-
Subsystem(子系统类):各个独立的子系统,完成具体的业务逻辑。
示例
// 外观类:简化多个功能操作的接口
class SmartphoneFacade {private Camera camera = new Camera();private MusicPlayer musicPlayer = new MusicPlayer();public void takePhotoAndPlayMusic() {camera.open();camera.takePhoto();musicPlayer.play();}public void stopMusicAndCloseCamera() {musicPlayer.stop();camera.open(); }
}// 子系统相机
class Camera {public void open() {System.out.println("打开相机");}public void takePhoto() {System.out.println("拍照");}
}
// 子系统音乐播放器类似// 客户端
public class FacadePatternExample {public static void main(String[] args) {SmartphoneFacade smartphone = new SmartphoneFacade();smartphone.takePhotoAndPlayMusic();smartphone.stopMusicAndCloseCamera();}
}
使用手机只需要调用相关方法,而不用去管Camera
和MusicPlayer
的具体实现
使用场景
- 当系统较为复杂时,使用外观模式可以简化与子系统的交互,提供一个更易于使用的接口。
- 为多个子系统提供一个统一的接口。
- 需要解耦子系统与外部代码的依赖关系。
优点:
- 简化接口:隐藏了系统的复杂性,提供了更简单的接口。
- 降低耦合:客户端与子系统之间的耦合度降低,修改子系统的实现不会影响到客户端。
Context
Context
封装了很多重要的操作,如startActivity
、sendBroadcast
、bindService
等。因此,Context
对开发者来说是最重要的高层接口。Context
只是一个定义了很多接口的抽象类,这些接口的功能实现并不是在Context
及其子类中,而是通过其他子系统来完成。
Context
只是一个抽象类,它的真正实现在Contextlmpl
类中,Contextlmpl
就是外观类。
startActivity()
:startActivity()
方法启动一个新的Activity
,但实际的启动过程是通过ActivityManagerService
来完成的。sendBroadcast()
:底层的实现则是通过BroadcastManager
来处理。bindService()
:bindService()
方法允许应用与服务建立连接,实际操作是通过ServiceManager
来管理服务的绑定。
享元模式
享元模式用来尽可能减少内存使用量,它适合用于可能存在大量重复对象的场景,来缓存可共享的对象,达到对象共享、避免创建过多对象的效果,可以提升性能、避免内存移除等。
核心思想是复用已经存在的对象,而不是每次都创建新对象。
享元对象中的部分状态是可以共享,可以共享的状态成为内部状态,内部状态不会随着环境变化;不可共享的状态则称为外部状态,它们会随着环境的改变而改变。
在享元模式中会建立一个对象容器,在经典的享元模式中该容器为一个 Map,它的键是享元对象的内部状态,它的值就是享元对象本身。客户端程序通过这个内部状态从享元工厂中获取享元对象,如果有缓存则使用缓存对象,否则创建一个享元对象并且存入容器中,这样一来就避免了创建过多对象的问题。
结构
- 抽象享元(Flyweight):定义享元对象的基类或接口
- 具体享元(ConcreteFlyweight):实现抽象享元对象
- 享元工厂(FlyweightFactory):用于管理享元对象的创建和共享,确保享元对象的复用。
示例
在火车票预订系统中,有很多用户会购买相同类型、相同时间、相同座位的火车票。每次有用户购买相同的火车票时,我们不需要为每个用户创建一个新的火车票对象,而是可以共享相同的火车票对象。享元模式可以帮助我们避免为每个用户创建重复的对象,从而节省内存。
享元对象(Flyweight)
TrainTicket
类是享元对象,表示火车票的固定信息,例如车次、出发时间、座位类型等。
// 享元类:火车票
public class TrainTicket {private String trainNumber; // 车次private String departureTime; // 出发时间// 构造方法,初始化火车票的固定信息public TrainTicket(String trainNumber, String departureTime) {this.trainNumber = trainNumber;this.departureTime = departureTime;}// 购票public void serve(String passengerName) {System.out.println("乘车人:" + passengerName + " ,车次 " + trainNumber +",发车时间: " + departureTime);}
}
享元工厂(FlyweightFactory)
TrainTicketFactory
类是享元工厂,用于管理和共享火车票对象。
public class TrainTicketFactory {private Map<String, TrainTicket> ticketMap = new HashMap<>();public TrainTicket getTrainTicket(String trainNumber, String departureTime) {String key = trainNumber + departureTime;if (!ticketMap.containsKey(key)) {ticketMap.put(key, new TrainTicket(trainNumber, departureTime));System.out.println("购票成功,乘车人:" + trainNumber + ", 车次: " + departureTime);}return ticketMap.get(key); // 返回共享的火车票对象}
}
客户端
public class TrainStation {public static void main(String[] args) {TrainTicketFactory ticketFactory = new TrainTicketFactory();TrainTicket ticket1 = ticketFactory.getTrainTicket("G101", "10:00");TrainTicket ticket2 = ticketFactory.getTrainTicket("G101", "10:00");TrainTicket ticket3 = ticketFactory.getTrainTicket("D202", "14:00");ticket1.serve("Alice");ticket2.serve("Bob");ticket3.serve("Charlie");}
}
TrainTicket
类是享元对象,火车票的固定信息,相同的,可以被多个乘客共享。TrainTicketFactory
类是享元工厂,维护了一个火车票对象池,确保每种车次、出发时间和座位类型的火车票只创建一次。如果有相同的请求,返回已有的火车票对象。TrainStation
类模拟了多个乘客购买相同车次、相同时间的火车票。由于使用了享元模式,虽然有多个乘客,但共享了相同的火车票对象。
使用场景
- 对象创建代价高,且每个对象的内部状态差别不大。
- 需要优化程序性能,减少内存消耗。
Message
在Handler中,使用对象池来管理 Message
对象,能够有效避免频繁的对象创建,减少内存占用和GC频率。
public final class Message implements Parcelable {Message next;// 同步锁的对象public static final Object sPoolSync = new Object();// 对象池的头部,具体实现是链表private static Message sPool;// 当前池中存储的 Message 对象数量private static int sPoolSize = 0;// 对象池的最大容量private static final int MAX_POOL_SIZE = 50;public static Message obtain() {// 线程安全synchronized (sPoolSync) {if (sPool != null) {// 从池中取出一个 Message 对象Message m = sPool;// 更新池的头部为下一个对象sPool = m.next;// 清空当前 Message 对象的链表连接,避免不必要的引用m.next = null;// 清除消息的标志位,表示该对象已被重用m.flags = 0;// 更新池中存储的对象数量sPoolSize--;return m;}}// 如果池中没有可复用的对象,创建一个新的 Message 对象并返回return new Message();}/*** 回收该 Message 对象,准备将其放入对象池中。* 该方法会检查 Message 是否还在使用中,若仍在使用则抛出异常。*/public void recycle() {if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it "+ "is still in use.");}return; }recycleUnchecked();}/*** 实际进行回收操作,将该对象状态清空,并加入对象池中以供复用。*/void recycleUnchecked() {// 清除对象的各个字段,确保该对象回收后不再持有任何引用flags = FLAG_IN_USE;what = 0;arg1 = 0;arg2 = 0;obj = null;replyTo = null;sendingUid = UID_NONE;workSourceUid = UID_NONE;when = 0;target = null;callback = null;data = null;synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; }}}
}
Message
类承担了三个职责:
- Flyweight 抽象角色:提供了统一的接口来处理对象的共享和复用。
- ConcreteFlyweight 具体享元角色:实际存储对象的状态并处理业务逻辑。
- FlyweightFactory 工厂角色:管理对象池,复用和回收
Message
对象。
参考:
- 《设计模式之禅》
- 《Android进阶之光》
- 《Android源码设计模式解析与实战》