有个大神写的很好:
参考:设计模式学习笔记(四:策略模式)
参考:设计模式学习笔记(二:观察者模式)
参考:设计模式学习笔记-代理模式
参考:设计模式--装饰者模式与代理模式(重要)
参考:设计模式——代理模式与装饰模式的异同 (重要)
参考:设计模式之装饰模式
参考:java模式—装饰者模式
参考:修饰者模式(装饰者模式,Decoration)
一、策略模式:
(1)解决场景:
某个功能有多个方案可以实现,要达到某个目的,需要根据具体的实际情况,选择合适的方法,
(2)如何实现:
分为两个层次
(a) 环境类
环境类负责接收用户的请求,并根据实际情况把相应的请求委托给一组策略类中的一个;
(b) 一组策略类
一组封装了具体的实现类的算法,并负责具体的计算过程
(3)关键类图:
首先定义策略类的接口:
public interface GameStrategy {public void goldCoin();}
其次 实现一组策略类:
public class normalStrategy implements GameStrategy { @Override public void goldCoin() { // System.out.println("普通玩家没有折扣"); } } public class advancedStrategy implements GameStrategy {@Overridepublic void goldCoin() {// TODO Auto-generated method stubSystem.out.println("高级会员9折优惠");} }
环境类的实现:
public class Context { //持有一个Strategy的引用 private GameStrategy mGameStrategy; // 构造函数 public Context(GameStrategy strategy) { this.mGameStrategy = strategy; } public void setStrategy(GameStrategy strategy) { this.mGameStrategy = strategy; } public void goldCoin() { this.mGameStrategy.goldCoin(); } }
最后根据不同的情况进行调用:
public class main { public static void main(String[] args) { //普通玩家策略 Context context = new Context(new normalStrategy()); context.goldCoin(); //高级玩家策略 context.setStrategy(new advancedStrategy()); context.goldCoin(); } }
二、观察者模式
(1)解决场景:
(a) 对一个对象状态的更新,需要其他对象同步更新
(b) 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节
观察者模式,又称为发布订阅模式,它的特点:
(a) subject 和 observer之间是松耦合的,各自独立实现,
(b) subject在发送广播通知的时候,不需要指定具体的observer,observer可以自行决定是否要订阅
(c) 遵循常用设计原则:高内聚,低耦合
(2)如何实现:
关键类图:
定义观察者接口:
public interface Observer {void update(String message,String name);}
实现观察者:
public class Bianyi1 implements Observer { //定义姓名 private String bname = "张昊天"; @Override public void update(String message,String name) { System.out.println(bname+":"+name+"那里有新情况:"+ message); } } public class Bianyi2 implements Observer { //定义姓名 private String bname = "石破天"; @Override public void update(String message,String name) { System.out.println(bname+":"+name+"那里有新情况:"+ message); } }
定义目标接口:
public interface Huairen { //添加便衣观察者 void addObserver(Observer observer); //移除便衣观察者 void removeObserver(Observer observer); //通知观察者 void notice(String message); }
两个实现目标的类:
import java.util.*; /** * 嫌犯大熊 */ public class XianFan1 implements Huairen { //别称 private String name = "大熊"; //定义观察者集合 private List<Observer> observerList = new ArrayList<Observer>(); //增加观察者 @Override public void addObserver(Observer observer) { if(!observerList.contains(observer)){ observerList.add(observer); } } //移除观察者 @Override public void removeObserver(Observer observer) { if(observerList.contains(observer)){ observerList.remove(observer); } } //通知观察者 @Override public void notice(String message) { for(Observer observer:observerList){ observer.update(message,name); } } } import java.util.*; /** * 嫌犯黑狗 */ public class XianFan2 implements Huairen { //别称 private String name = "黑狗"; //定义观察者集合 private List<Observer> observerList = new ArrayList<Observer>(); //增加观察者 @Override public void addObserver(Observer observer) { if(!observerList.contains(observer)){ observerList.add(observer); } } //移除观察者 @Override public void removeObserver(Observer observer) { if(observerList.contains(observer)){ observerList.remove(observer); } } //通知观察者 @Override public void notice(String message) { for(Observer observer:observerList){ observer.update(message,name); } } }
观察者根据目标进行调用:
public class Clienter { public static void main(String[] args) { //定义两个嫌犯 Huairen xf1 = new XianFan1(); Huairen xf2 = new XianFan2(); //定义三个观察便衣警察 Observer o1 = new Bianyi1(); Observer o2 = new Bianyi2(); //为嫌犯增加观察便衣 xf1.addObserver(o1); xf1.addObserver(o2); xf2.addObserver(o1); xf2.addObserver(o3); //定义嫌犯1的情况 String message1 = "又卖了一批货"; String message2 = "老大要下来视察了"; xf1.notice(message1); xf2.notice(message2); } }
三、代理模式:
(1)解决场景:
(a) 当我们想要隐藏某个类时,可以为其提供代理类
(b) 当一个类需要对不同的调用者提供不同的调用权限时,可以使用代理类来实现
(c) 当我们要扩展某个类的某个功能时,可以使用代理模式,在代理类中进行简单扩展
(2) 如何实现:
(a) 代理类与委托类实现同一接口
(b) 在委托类中实现功能,在代理类的方法中中引用委托类的同名方法
(c) 外部类调用委托类某个方法时,直接以接口指向代理类的实例,这正是代理的意义所在:屏蔽。
(3) 类图:
(a) 抽象的主题:
public interface Moveable {void move() throws Exception; }
(b)真实主题:
public class Car implements Moveable {public void move() throws Exception {Thread.sleep(new Random().nextInt(1000));System.out.println("汽车行驶中…");} }
(c) 事务处理器:
public class TimeHandler implements InvocationHandler {private Object target;public TimeHandler(Object target) {super();this.target = target;}/*** 参数:*proxy 被代理的对象*method 被代理对象的方法*args 方法的参数* 返回:*Object 方法返回值*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {long startTime = System.currentTimeMillis();System.out.println("汽车开始行驶…");method.invoke(target, args);long stopTime = System.currentTimeMillis();System.out.println("汽车结束行驶…汽车行驶时间:" + (stopTime - startTime) + "毫秒!");return null;}}
(d) 调用:
public class Test {public static void main(String[] args) throws Exception{Car car = new Car();InvocationHandler h = new TimeHandler(car);Class<?> cls = car.getClass();/***loader 类加载器*interfaces 实现接口*h InvocationHandler*/Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);m.move();}
四、装饰模式:
装饰者(decorator)模式:在不改变对象自身的基础上,在程序运行期间给对像动态的添加职责。与继承相比,装饰者是一种更轻便灵活的做法。
装饰者模式的特点
可以动态的给某个对象添加额外的职责,而不会影响从这个类中派生的其它对象;
(1)应用场景:
(a)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
(b) 需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
(2)实现的例子:
最常见的就是输入输出流:
BufferedReader in1 = new BufferedReader(new InputStreamReader(new FileInputStream(file)));//字符流DataInputStream in2 = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));//字节流// DataInputStream-从数据流读取字节,并将它们装换为正确的基本类型值或字符串// BufferedInputStream-可以通过减少读写次数来提高输入和输出的速度
public class People {public void wearShirt(){System.out.println("******穿衬衫******");} public void wearTrouser(){System.out.println("******穿西服裤******");}public void wearShoes(){System.out.println("******穿皮鞋******");}}
public class Client { public static void main(String[] args) {People people = new People();people.wearShirt();people.wearTrouser();people.wearShoes();} }
public interface People { public void wearClothing(); }
public class Xiaoming implements People{ private String name; public Xiaoming(){name = "小明";} public void wearClothing(){System.out.println(name+"******开始穿衣服******");}public String getName() {return name;}}
public abstract class Finery implements People { protected People people; public Finery(People people){this.people = people;}public abstract void wearClothing();}
public class ShirtFinery extends Finery {public ShirtFinery(People people){super(people);}@Overridepublic void wearClothing() {people.wearClothing();System.out.println("******穿衬衫******");} }
public class ShoesFinery extends Finery { public ShoesFinery(People people){super(people);}@Overridepublic void wearClothing() {people.wearClothing();System.out.println("******穿皮鞋*******");} }public class TrouserFinery extends Finery {public TrouserFinery(People people){super(people);}@Overridepublic void wearClothing() {people.wearClothing();System.out.println("******穿西服裤*******");} }
public class Client { public static void main(String[] args) {People people = new Xiaoming();Finery shirtFinery = new ShirtFinery(people);Finery trouserFinery = new TrouserFinery(shirtFinery);Finery shoesFinery = new ShoesFinery(trouserFinery);shoesFinery.wearClothing();}}
(3)类图:
People是定义了一个接口,用来添加具体的职责,而Xiaoming是具体的People,也就是被装饰的对象。对于Finery实现了People接口,进而对People进行扩展,而Finery的子类就是具体的装饰类,Finery中依赖People类,装饰的具体对象。 这就是所谓的装饰者模式。如果我要添加穿袜子的步骤,则只需要再添加一个实现类,完全不需要修改其他代码(Client是客户端类,肯定是要修改的)。
装饰者模式和代理模式的区别:
两种模式的特点
装饰模式:
在不改变接口的前提下,动态扩展对象的访问。
动态继承,让类具有在运行期改变行为的能力。
装饰模式,突出的是运行期增加行为,这和继承是不同的,继承是在编译期增加行为。
强调:增强,新增行为
代理模式:
在不改变接口的前提下,控制对象的访问。
1.从封装的角度讲,是为了解决类与类之间相互调用而由此导致的耦合关系,可以说是接口的另外一个层引用。
比如:在a类->b代理->c类这个关系中,c类的一切行为都隐藏在b中。即调用者不知道要访问的内容与代理了什么对象。
2.从复用的角度讲,可以解决不同类调用一个复杂类时,仅仅因较小的改变而导致整个复杂类新建一个类。
比如:a类->c类1;b类->c类2。
可以变为a类->ca代理类->c类;b类->cb代理类-c类。
代理模式,是类之间的封装和(某方面的)复用。
强调:限制,控制访问
总结:
1. 装饰模式可以让使用者直观的看到增强了哪些功能,而代理模式完全限制了使用者。
2. 对装饰模式来说,装饰者(Decorator)和被装饰者(Cafe)都实现同一个 接口。
3. 对代理模式来说,代理类(Proxy Class)和真实处理的类(Real Class)都实现同一个接口。
4. 此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。
装饰模式与继承的比较
明显的,装饰模式可以动态的扩展对象的行为。
比如某对象有30项行为,但是在第一阶段用到1-20行为,第二阶段用到11-30项行为,所以这时候,就可以只定义11-20的行为。
在第一阶段运行时,可以将1-10的行为以“装饰1”给加上
到第二阶段运行时,可以将“装饰1”去掉,将21-30的能以“装饰2”给加上。
但是继承是在编译期增加行为。
装饰模式的优缺点
优点:
1. 装饰模式可以提供比继承更多地灵活性。
2. 可以通过一种动态的方式来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
3. 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
4. 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。
缺点:
1. 会产生很多的小对象(具体装饰类),增加了系统的复杂性。
2. 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。