【设计模式】结构型设计模式总结之代理模式、装饰模式、外观模式、享元模式
代理模式
代理模式(Proxy Pattern) 是一种结构型设计模式,它提供了一个代理对象,控制对目标对象的访问。代理对象通常在客户端与目标对象之间起到中介的作用,用于扩展目标对象的功能。
定义:为其他对象提供一种代理以控制对这个对象的访问
示例
定义接口
// 公共接口
public interface IShop {
void buy();
}
实现真实类
// 真实对象
public class Buyer implements IShop{
@Override
public void buy() {
System.out.println("购买");
}
}
定义代理类
// 代理类
public class Buying implements IShop{
private IShop iShop;
public Buying(IShop iShop) {
this.iShop = iShop;
}
@Override
public 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 {
@Override
public void request() {
System.out.println("真实主题:处理请求。");
}
}
// 动态代理的处理器
public class DynamicProxyHandler implements InvocationHandler {
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
@Override
public 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 {
@Override
public void operate() {
System.out.println("执行基本操作");
}
}
// 装饰者基类
public abstract class Decorator extends Component {
protected Component component; // 持有组件对象的引用
public Decorator(Component component) {
this.component = component;
}
@Override
public void operate() {
component.operate(); // 调用组件的操作
}
}
// 具体装饰者实现类
public class ConcreteDecoratorA extends Decorator {
public LoggingDecorator(Component component) {
super(component);
}
@Override
public 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{
@Override
public void startActivity(Intent intent) {
warnIfCallingFromSystemProcess();
startActivity(intent, null);
}
@Override
public 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;
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}
@Override
public 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源码设计模式解析与实战》
原文地址:https://blog.csdn.net/Patrick_yuxuan/article/details/144333737
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!