面向对象-设计模式-结构型
一年好景君须记,最是橙黄橘绿时。
简介:面向对象-设计模式-结构型。
一、概述
何谓设计模式:
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
设计模式的好处&学习目的:
1、为了代码可重用行、让代码更易被他人理解、保证代码的可靠性、使代码编写真正实现工程化;
2、设计模式便于我们维护项目,增强系统的健壮性和可扩展性;
3、设计模式还可以锻炼码农的设计思维、升华代码质量等。
二、结构型
适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式、代理模式。
1. 适配器(Adapter)
Intent
把一个类接口转换成另一个用户需要的接口。
Class Diagram
Implementation
鸭子(Duck)和火鸡(Turkey)拥有不同的叫声,Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。
要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法,从而让火鸡冒充鸭子!
1 public interface Duck {
2 void quack();
3 }
1 public interface Turkey {
2 void gobble();
3 }
1 public class WildTurkey implements Turkey {
2 @Override
3 public void gobble() {
4 System.out.println("gobble!");
5 }
6 }
1 public class TurkeyAdapter implements Duck {
2 Turkey turkey;
3
4 public TurkeyAdapter(Turkey turkey) {
5 this.turkey = turkey;
6 }
7
8 @Override
9 public void quack() {
10 turkey.gobble();
11 }
12 }
1 public class Client {
2 public static void main(String[] args) {
3 Turkey turkey = new WildTurkey();
4 Duck duck = new TurkeyAdapter(turkey);
5 duck.quack();
6 }
7 }
2. 桥接(Bridge)
Intent
将抽象与实现分离开来,使它们可以独立变化。
Class Diagram
- Abstraction:定义抽象类的接口
- Implementor:定义实现类接口
Implementation
RemoteControl 表示遥控器,指代 Abstraction。
TV 表示电视,指代 Implementor。
桥接模式将遥控器和电视分离开来,从而可以独立改变遥控器或者电视的实现。
1 public abstract class TV {
2 public abstract void on();
3
4 public abstract void off();
5
6 public abstract void tuneChannel();
7 }
1 public class Sony extends TV {
2 @Override
3 public void on() {
4 System.out.println("Sony.on()");
5 }
6
7 @Override
8 public void off() {
9 System.out.println("Sony.off()");
10 }
11
12 @Override
13 public void tuneChannel() {
14 System.out.println("Sony.tuneChannel()");
15 }
16 }
1 public class RCA extends TV {
2 @Override
3 public void on() {
4 System.out.println("RCA.on()");
5 }
6
7 @Override
8 public void off() {
9 System.out.println("RCA.off()");
10 }
11
12 @Override
13 public void tuneChannel() {
14 System.out.println("RCA.tuneChannel()");
15 }
16 }
1 public abstract class RemoteControl {
2 protected TV tv;
3
4 public RemoteControl(TV tv) {
5 this.tv = tv;
6 }
7
8 public abstract void on();
9
10 public abstract void off();
11
12 public abstract void tuneChannel();
13 }
1 public class ConcreteRemoteControl1 extends RemoteControl {
2 public ConcreteRemoteControl1(TV tv) {
3 super(tv);
4 }
5
6 @Override
7 public void on() {
8 System.out.println("ConcreteRemoteControl1.on()");
9 tv.on();
10 }
11
12 @Override
13 public void off() {
14 System.out.println("ConcreteRemoteControl1.off()");
15 tv.off();
16 }
17
18 @Override
19 public void tuneChannel() {
20 System.out.println("ConcreteRemoteControl1.tuneChannel()");
21 tv.tuneChannel();
22 }
23 }
1 public class ConcreteRemoteControl2 extends RemoteControl {
2 public ConcreteRemoteControl2(TV tv) {
3 super(tv);
4 }
5
6 @Override
7 public void on() {
8 System.out.println("ConcreteRemoteControl2.on()");
9 tv.on();
10 }
11
12 @Override
13 public void off() {
14 System.out.println("ConcreteRemoteControl2.off()");
15 tv.off();
16 }
17
18 @Override
19 public void tuneChannel() {
20 System.out.println("ConcreteRemoteControl2.tuneChannel()");
21 tv.tuneChannel();
22 }
23 }
1 public class Client {
2 public static void main(String[] args) {
3 RemoteControl remoteControl1 = new ConcreteRemoteControl1(new RCA());
4 remoteControl1.on();
5 remoteControl1.off();
6 remoteControl1.tuneChannel();
7 RemoteControl remoteControl2 = new ConcreteRemoteControl2(new Sony());
8 remoteControl2.on();
9 remoteControl2.off();
10 remoteControl2.tuneChannel();
11 }
12 }
3. 组合(Composite)
Intent
将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。
Class Diagram
组件(Component)类是组合类(Composite)和叶子类(Leaf)的父类,可以把组合类看成是树的中间节点。
组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。
Implementation
1 public abstract class Component {
2 protected String name;
3
4 public Component(String name) {
5 this.name = name;
6 }
7
8 public void print() {
9 print(0);
10 }
11
12 abstract void print(int level);
13
14 abstract public void add(Component component);
15
16 abstract public void remove(Component component);
17 }
1 public class Composite extends Component {
2
3 private List<Component> child;
4
5 public Composite(String name) {
6 super(name);
7 child = new ArrayList<>();
8 }
9
10 @Override
11 void print(int level) {
12 for (int i = 0; i < level; i++) {
13 System.out.print("--");
14 }
15 System.out.println("Composite:" + name);
16 for (Component component : child) {
17 component.print(level + 1);
18 }
19 }
20
21 @Override
22 public void add(Component component) {
23 child.add(component);
24 }
25
26 @Override
27 public void remove(Component component) {
28 child.remove(component);
29 }
30 }
1 public class Leaf extends Component {
2 public Leaf(String name) {
3 super(name);
4 }
5
6 @Override
7 void print(int level) {
8 for (int i = 0; i < level; i++) {
9 System.out.print("--");
10 }
11 System.out.println("left:" + name);
12 }
13
14 @Override
15 public void add(Component component) {
16 throw new UnsupportedOperationException(); // 牺牲透明性换取单一职责原则,这样就不用考虑是叶子节点还是组合节点
17 }
18
19 @Override
20 public void remove(Component component) {
21 throw new UnsupportedOperationException();
22 }
23 }
1 public class Client {
2 public static void main(String[] args) {
3 Composite root = new Composite("root");
4 Component node1 = new Leaf("1");
5 Component node2 = new Composite("2");
6 Component node3 = new Leaf("3");
7 root.add(node1);
8 root.add(node2);
9 root.add(node3);
10 Component node21 = new Leaf("21");
11 Component node22 = new Composite("22");
12 node2.add(node21);
13 node2.add(node22);
14 Component node221 = new Leaf("221");
15 node22.add(node221);
16 root.print();
17 }
18 }
1 输出:
2 Composite:root
3 --left:1
4 --Composite:2
5 ----left:21
6 ----Composite:22
7 ------left:221
8 --left:3
4. 装饰(Decorator)
Intent
为对象动态添加功能。
Class Diagram
装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。
Implementation
设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。
下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。
1 public interface Beverage {
2 double cost();
3 }
1 public class DarkRoast implements Beverage {
2 @Override
3 public double cost() {
4 return 1;
5 }
6 }
1 public class HouseBlend implements Beverage {
2 @Override
3 public double cost() {
4 return 1;
5 }
6 }
1 public abstract class CondimentDecorator implements Beverage {
2 protected Beverage beverage;
3 }
1 public class Milk extends CondimentDecorator {
2
3 public Milk(Beverage beverage) {
4 this.beverage = beverage;
5 }
6
7 @Override
8 public double cost() {
9 return 1 + beverage.cost();
10 }
11 }
1 public class Mocha extends CondimentDecorator {
2
3 public Mocha(Beverage beverage) {
4 this.beverage = beverage;
5 }
6
7 @Override
8 public double cost() {
9 return 1 + beverage.cost();
10 }
11 }
1 public class Client {
2
3 public static void main(String[] args) {
4 Beverage beverage = new HouseBlend();
5 beverage = new Mocha(beverage);
6 beverage = new Milk(beverage);
7 System.out.println(beverage.cost()); // 3.0
8 }
9 }
设计原则
类应该对扩展开放,对修改关闭:也就是添加新功能时不需要修改代码。饮料可以动态添加新的配料,而不需要去修改饮料的代码。
不可能把所有的类设计成都满足这一原则,应当把该原则应用于最有可能发生改变的地方。
5. 外观(Facade)
Intent
提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。
Class Diagram
Implementation
观看电影需要操作很多电器,使用外观模式实现一键看电影功能。
1 public class SubSystem {
2 public void turnOnTV() {
3 System.out.println("turnOnTV()");
4 }
5
6 public void setCD(String cd) {
7 System.out.println("setCD( " + cd + " )");
8 }
9
10 public void startWatching(){
11 System.out.println("startWatching()");
12 }
13 }
1 public class Facade {
2 private SubSystem subSystem = new SubSystem();
3
4 public void watchMovie() {
5 subSystem.turnOnTV();
6 subSystem.setCD("a movie");
7 subSystem.startWatching();
8 }
9 }
1 public class Client {
2 public static void main(String[] args) {
3 Facade facade = new Facade();
4 facade.watchMovie();
5 }
6 }
设计原则
最少知道原则:只和你的密友谈话。也就是说客户对象所需要交互的对象应当尽可能少。
6. 享元(Flyweight)
Intent
利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。
Class Diagram
- Flyweight:享元对象
- IntrinsicState:内部状态,享元对象共享内部状态
- ExtrinsicState:外部状态,每个享元对象的外部状态不同
Implementation
1 public interface Flyweight {
2 void doOperation(String extrinsicState);
3 }
1 public class ConcreteFlyweight implements Flyweight {
2
3 private String intrinsicState;
4
5 public ConcreteFlyweight(String intrinsicState) {
6 this.intrinsicState = intrinsicState;
7 }
8
9 @Override
10 public void doOperation(String extrinsicState) {
11 System.out.println("Object address: " + System.identityHashCode(this));
12 System.out.println("IntrinsicState: " + intrinsicState);
13 System.out.println("ExtrinsicState: " + extrinsicState);
14 }
15 }
1 public class FlyweightFactory {
2
3 private HashMap<String, Flyweight> flyweights = new HashMap<>();
4
5 Flyweight getFlyweight(String intrinsicState) {
6 if (!flyweights.containsKey(intrinsicState)) {
7 Flyweight flyweight = new ConcreteFlyweight(intrinsicState);
8 flyweights.put(intrinsicState, flyweight);
9 }
10 return flyweights.get(intrinsicState);
11 }
12 }
1 public class Client {
2
3 public static void main(String[] args) {
4 FlyweightFactory factory = new FlyweightFactory();
5 Flyweight flyweight1 = factory.getFlyweight("aa");
6 Flyweight flyweight2 = factory.getFlyweight("aa");
7 flyweight1.doOperation("x");
8 flyweight2.doOperation("y");
9 }
10 }
1 输出:
2 Object address: 1163157884
3 IntrinsicState: aa
4 ExtrinsicState: x
5 Object address: 1163157884
6 IntrinsicState: aa
7 ExtrinsicState: y
JDK
Java 利用缓存来加速大量小对象的访问时间。
- java.lang.Integer#valueOf(int)
- java.lang.Boolean#valueOf(boolean)
- java.lang.Byte#valueOf(byte)
- java.lang.Character#valueOf(char)
7. 代理(Proxy)
Intent
控制对其它对象的访问。
Class Diagram
代理有以下四类:
- 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。
- 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。
- 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。
- 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。
Implementation
以下是一个虚拟代理的实现,模拟了图片延迟加载的情况下使用与图片大小相等的临时内容去替换原始图片,直到图片加载完成才将图片显示出来。
1 public interface Image {
2 void showImage();
3 }
1 public class HighResolutionImage implements Image {
2
3 private URL imageURL;
4 private long startTime;
5 private int height;
6 private int width;
7
8 public int getHeight() {
9 return height;
10 }
11
12 public int getWidth() {
13 return width;
14 }
15
16 public HighResolutionImage(URL imageURL) {
17 this.imageURL = imageURL;
18 this.startTime = System.currentTimeMillis();
19 this.width = 600;
20 this.height = 600;
21 }
22
23 public boolean isLoad() {
24 // 模拟图片加载,延迟 3s 加载完成
25 long endTime = System.currentTimeMillis();
26 return endTime - startTime > 3000;
27 }
28
29 @Override
30 public void showImage() {
31 System.out.println("Real Image: " + imageURL);
32 }
33 }
1 public class ImageProxy implements Image {
2
3 private HighResolutionImage highResolutionImage;
4
5 public ImageProxy(HighResolutionImage highResolutionImage) {
6 this.highResolutionImage = highResolutionImage;
7 }
8
9 @Override
10 public void showImage() {
11 while (!highResolutionImage.isLoad()) {
12 try {
13 System.out.println("Temp Image: " + highResolutionImage.getWidth() + " " + highResolutionImage.getHeight());
14 Thread.sleep(100);
15 } catch (InterruptedException e) {
16 e.printStackTrace();
17 }
18 }
19 highResolutionImage.showImage();
20 }
21 }
1 public class ImageViewer {
2
3 public static void main(String[] args) throws Exception {
4 String image = "http://www.ttt.com/image.jpg";
5 URL url = new URL(image);
6 HighResolutionImage highResolutionImage = new HighResolutionImage(url);
7 ImageProxy imageProxy = new ImageProxy(highResolutionImage);
8 imageProxy.showImage();
9 }
10 }
JDK
- java.lang.reflect.Proxy
- RMI(Remote Method Invocation 远程方法调用)
一年好景君须记
最是橙黄橘绿时