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)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。


InputStreamByteArrayInputStream、PipedInputStream、StringBufferInputStream等原始流处理器。FilterInputStreamDateInputStream、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。
InvocationHandlerProxy.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");
}
}