Ingram 2019-06-29
适配器模式把一个类的接口变换成客户端所期待的另一种接口,使得原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
interface Target { void operation1(); void operation2(); } class Adaptee { public void operation1() {} } class Adapter extends Adaptee implements Target { @Override public void operation2() {} }
interface Target { void operation1(); void operation2(); } class Adaptee { public void operation1() {} } class Adapter implements Target { private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public void operation1() { adaptee.operation1(); } @Override public void operation2() { // do something } }
// 充电器只能接受USB接口 public class Charger { public static void main(String[] args) throws Exception{ USB usb = new SuperAdapter(new TypeC()); connect(usb); usb = new SuperAdapter(new Lightning()); connect(usb); } public static void connect(USB usb) { usb.power(); usb.data(); } } // 充电器的接口都是USB的,假设有两个方法分别是电源和数据 interface USB { void power(); void data(); } // IOS的Lightning接口 class Lightning { void iosPower() { System.out.println("IOS Power"); } void iosData() { System.out.println("IOS Data"); } } // TYPE-C接口 class TypeC { void typeCPower() { System.out.println("TypeC Power"); } void typeCData() { System.out.println("TypeC Data"); } } // 超级适配器,可以适配多种手机机型 class SuperAdapter implements USB { private Object obj; public SuperAdapter(Object obj) { this.obj = obj; } @Override public void power() { if(obj.getClass() == Lightning.class) { ((Lightning)obj).iosPower(); } else if(obj.getClass() == TypeC.class) { ((TypeC)obj).typeCPower(); } } @Override public void data() { if(obj.getClass() == Lightning.class) { ((Lightning)obj).iosData(); } else if(obj.getClass() == TypeC.class) { ((TypeC)obj).typeCData(); } } }
缺省适配模式为一个接口提供缺省实现,这样子类型可以从这个缺省实现进行扩展,而不必从原有接口进行扩展。
Monk
接口定义了两个方法,于是它的子类必须实现这两个方法。LuZhiShen
,他只能实现一部分方法,另一部分方法无法实现MonkAdapter
实现此Monk
接口,此抽象类给接口所有方法都提供一个空的方法,LuZhiShen
只需要继承该适配类即可。// 和尚 interface Monk { void practiceKungfu(); void chantPrayer(); } abstract class MonkAdapter implements Monk { @Override public void practiceKungfu() {} @Override public void chantPrayer() {} } class LuZhiShen extends MonkAdapter { @Override public void practiceKungfu() { System.out.println("拳打镇关西"); } }
缺省适配器模式可以使所需要的类不必实现不需要的接口。
组合模式,就是在一个对象中包含其他对象,这些被包含的对象可能是终点对象(不再包含别的对象),也有可能是非终点对象(其内部还包含其他对象)。
我们将对象称为节点,即一个根节点包含许多子节点,这些子节点有的不再包含子节点,而有的仍然包含子节点,以此类推。很明显,这是树形结构,终结点叫叶子节点,非终节点叫树枝节点,第一个节点叫根节点。
安全式的合成模式要求管理集合的方法只出现在树枝结点(Composite)中,而不出现在树叶结点中。
透明的合成模式要求所有的具体构建类,都符合一个固定的接口。
装饰器模式(Decorator)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
InputStream
ByteArrayInputStream
、PipedInputStream
、StringBufferInputStream
等原始流处理器。FilterInputStream
DateInputStream
、BufferedInputStream
、LineNumberInputStream
也用到类装饰器模式
// Component:一个艺人 interface Artist { void show(); } // Concrete Component:一个歌手 class Singer implements Artist { @Override public void show() { System.out.println("Let It Go"); } } // 装饰后的歌手:不仅会唱歌,还会讲笑话和跳舞 class SuperSinger implements Artist { private Artist role; public SuperSinger(Artist role) { this.role = role; } @Override public void show() { System.out.println("Tell Jokes!"); role.show(); System.out.println("Dance!"); } }
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象对引用。
public class Test { public static void main(String[] args) { Subject subject = new RealSubject(); Subject proxy = new ProxySubject(subject); proxy.request(); // 此处通过代理类来执行 } } interface Subject { void request(); } class RealSubject implements Subject { @Override public void request() { System.out.println("RealSubject"); } } class ProxySubject implements Subject { private Subject subject; public ProxySubject(Subject subject) { this.subject = subject; } @Override public void request() { System.out.println("ProxySubject"); } }
自从JDK 1.3以后,Java在java.lang.reflect
库中提供了一下三个类直接支持代理模式:Proxy
、InvocationHander
、Method
。
InvocationHandler
Proxy.newInstance()
invoke()
方法中执行相应操作public class Test { public static void main(String[] args) { // 创建要被代理的实例对象 Subject subject = new RealSubject(); // 创建一个与被代理实例对象有关的InvocationHandler InvocationHandler handler = new ProxySubject(subject); // 创建一个代理对象来代理subject,被代理的对象subject的每个方法执行都会调用代理对象proxySubject的invoke方法 Subject proxySubject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, handler); // 代理对象执行 proxySubject.request(); } } interface Subject { void request(); } class RealSubject implements Subject { @Override public void request() { System.out.println("RealSubject"); } } class ProxySubject implements InvocationHandler { private Subject subject; public ProxySubject(Subject subject) { this.subject = subject; } /** * @param proxy 要代理的 * @param method * @param args * @return */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before Proxy"); Object obj = method.invoke(subject, args); System.out.println("After Proxy"); return obj; } }
ProxySubject
适配器模式的用意是改变所考虑对象的接口,而代理模式不能改变。
享元模式以共享的方式高效地支持大量的细粒度对象。
单纯享元模式中,所有的享元对象都是可以共享的。
内蕴状态:是存储在享元对象内部的,不会随环境改变而改变的。一个享元可以具有内蕴状态并可以共享。
外蕴状态:随环境改变而改变、不可以共享的状态。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。
享元模式中的对象不一定非要是不变对象,但大多数享元对象的确是这么设计的。
减少对象的创建,降低内存消耗
依旧是熟悉的KFC点餐为例:
public class KFC { public static void main(String[] args) { OrderFactory orderFactory = OrderFactory.getInstance(); Order order = orderFactory.getOrder(Food.MiniBurger); order.operation("李雷"); order = orderFactory.getOrder(Food.MiniBurger); order.operation("韩梅梅"); } } enum Food { MiniBurger, MexicanTwister, CornSalad, HotWing, PepsiCola } // Flyweight角色 interface Order { // 传入的是外蕴对象:顾客 void operation(String customer); } // ConcreteFlyweight角色 class FoodOrder implements Order { // 内蕴状态 private Food food; // 构造方法,传入享元对象的内部状态的数据 public FoodOrder(Food food) { this.food = food; } @Override public void operation(String customer) { System.out.println("顾客[" + customer + "]点的是" + food.toString()); } } // FlyweightFactory角色 class OrderFactory { private Map<Food, Order> orderPool = new HashMap<>(); private static OrderFactory instance = new OrderFactory(); private OrderFactory() {} public static OrderFactory getInstance() { return instance; } // 获取Food对应的享元对象 public Order getOrder(Food food) { Order order = orderPool.get(food); if (null == order) { order = new FoodOrder(food); orderPool.put(food, order); } return order; } }
门面模式(Facade Pattern)要求一个子系统的外部与其内部通信,必须通过一个统一的门面对象进行。
门面模式没有一个一般化的类图描述,可以用下面的例子来说明。
通常只需要一个门面类,而且只有一个实例,因此可以设计称单例模式。当然也可有多个类。
MVC三层结构
假如没有服务员(门面),顾客(外部系统)要点一个套餐需要知道每个套餐包含的食物(子系统)种类,这样就会非常麻烦,所以最好的方式是直接告诉服务员套餐名称就好了。
public class Customer { public static void main(String[] args) { Waiter waiter = new Waiter(); List<Food> foodList = waiter.orderCombo("Combo1"); } } abstract class Food {} class MiniBurger extends Food {} class MexicanTwister extends Food {} class CornSalad extends Food {} class HotWing extends Food {} class PepsiCola extends Food {} class Waiter { public List<Food> orderCombo(String comboName) { List<Food> foodList; switch (comboName) { case "Combo1" : foodList = Arrays.asList(new MiniBurger(), new CornSalad(), new PepsiCola()); break; case "Combo2": foodList = Arrays.asList(new MexicanTwister(), new HotWing(), new PepsiCola()); break; default: foodList = new ArrayList<>(); } return foodList; } }
过滤器模式使用不同的条件过滤一组对象,并通过逻辑操作以解耦方式将其链接。这种类型的设计模式属于结构模式,因为该模式组合多个标准以获得单个标准。
Java8中的lambda表达式可以更简单的实现过滤器
List<Movie> movies = Stream.of( new Movie("大话西游","comedy"), new Movie("泰囧", "comedy"), new Movie("禁闭岛", "suspense")) .filter(var -> "comedy".equals(var.getType())) .collect(Collectors.toList());
Movie
,根据它的type
属性实现过滤Criteria
,规定过滤方法ComedyMovieCriteria
,根据comedy==movie.type
来过滤出需要的喜剧电影public class Test { public static void main(String[] args) { List<Movie> movies = new ArrayList(){{ add(new Movie("大话西游","comedy")); add(new Movie("泰囧", "comedy")); add(new Movie("禁闭岛", "suspense")); }}; System.out.println(new ComedyMovieCriteria().meetCriteria(movies)); } } // 被筛选的对象 class Movie { private String name; // 电影类型 private String type; public Movie(String name, String type) { this.name = name; this.type = type; } // getters & setters & toString } // 过滤器接口 interface Criteria { /** * @param movies 要被筛选的电影 * @return 筛选后的结果 */ List<Movie> meetCriteria(List<Movie> movies); } // 过滤喜剧电影的过滤器,要求是movie.type==comedy class ComedyMovieCriteria implements Criteria { @Override public List<Movie> meetCriteria(List<Movie> movies) { List<Movie> result = new ArrayList<>(); for (Movie movie : movies) { if ("comedy".equals(movie.getType())) { result.add(movie); } } return result; } }
桥接模式是将抽象化与实现化解耦,使两者可以独立地变化。桥接模式有助于理解面向对象的设计原则,包括开闭原则以及组合聚合复用原则。
抽象化:存在于多个实体中的共同的概念性联系;通过忽略一些信息,把不同的实体当作相同的实体来对待。
实现化:抽象化给出的具体实现就是实现化。一个类的实例就是这个类的实现化,一个子类就是它超类的实现化。
解耦:耦合就是两个实体的某种强关联,把它们的强关联去掉就是解耦。
强关联与弱关联:所谓强关联,就是在编译期已经确定的,无法在运行期动态改变的关联;所谓弱关联,就是可以动态地确定并且可以在运行期动态地改变的关联。继承是强关联,而聚合关系是弱关联。
桥接模式中的脱耦,就是在抽象化和实现化之间使用组合关系而不是继承关系,从而使两者可以相对独立的变化。
大多数的驱动器(Driver)都是桥接模式的应用,使用驱动程序的应用系统就是抽象化角色,而驱动器本身扮演实现化角色。
SendMsg
及其子类是按照发送消息的方式进行扩展的;而Send
是按照发送消息的时间进行扩展的,两者互不影响。Send
持有类一个SendMsg
对象,并可以使用此对象的方法。// Implementor角色 interface SendMsg { void sendMsg(); } // Concrete Implementor角色 class EmailSendMsg implements SendMsg { @Override public void sendMsg() { System.out.println("Send Msg By Email"); } } // Concrete Implementor角色 class WeChatSendMsg implements SendMsg { @Override public void sendMsg() { System.out.println("Send Msg By WeChat"); } } // Abstraction 角色 abstract class Send { protected SendMsg sendMsg; public Send(SendMsg sendMsg) { this.sendMsg = sendMsg; } public abstract void send(); } // Concrete Implementor角色 class ImmediatelySend extends Send { public ImmediatelySend(SendMsg sendMsg) { super(sendMsg); } @Override public void send() { sendMsg.sendMsg(); System.out.println("Send Msg Immediately"); } } // Concrete Implementor角色 class DelayedlySend extends Send { public DelayedlySend(SendMsg sendMsg) { super(sendMsg); } @Override public void send() { sendMsg.sendMsg(); System.out.println("Send Msg DelayedlySend"); } }