前言
上一篇设计模式一 中,我们只整理了创建型的设计模式,本篇主要看一下结构型的。
正文
结构型包含以下类型:
- 适配器 Adapter Class/Object
- 桥接 Bridge
- 组合 Composite
- 装饰 Decorator
- 外观 Facade
- 享元 Flyweight
- 代理 Proxy
适配器
个人意见:最适合在工作完成了一部分,但是两套系统接口规范不完全相适的情况下,使用适配器“协调”一下,在从零开始的情况下,尽可能避免接口不适配的情况。
demo:
目标
package adapter;
/**
* @ClassName: Target
* @Description: 当前系统中所期望的接口和api,也是类适配器需要“提供”的内容
* @date 2016年5月19日 下午1:13:29
*
* @author leobert.lan
* @version 1.0
*/
public interface Target {
void targetApi1();
void targetApi2();
}
已有模块
package adapter;
/**
* @ClassName: Adaptee
* @Description: 已有模块的入口,但和期待的不完全一致
* @date 2016年5月19日 下午1:15:38
*
* @author leobert.lan
* @version 1.0
*/
public class Adaptee {
public void Api1() {
System.out.println("call api1");
}
}
以类适配器模式实现
package adapter;
/**
* @ClassName: Adapter
* @Description: 类适配器模式demo
* @date 2016年5月19日 下午1:17:05
*
* @author leobert.lan
* @version 1.0
*/
public class Adapter extends Adaptee implements Target {
@Override
public void targetApi1() {
Api1();
}
@Override
public void targetApi2() {
System.out.println("targetApi2, do nothing,complete it for your business");
}
}
以对象适配器模式实现
package adapter;
/**
* @ClassName: Adapter2
* @Description: 对象适配器模式,相当于包装了一次
* @date 2016年5月19日 下午1:23:42
*
* @author leobert.lan
* @version 1.0
*/
public class Adapter2 implements Target{
private Adaptee adaptee;
public Adapter2(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void targetApi1() {
this.adaptee.Api1();
}
@Override
public void targetApi2() {
System.out.println("targetApi2, do nothing,complete it for your business");
}
}
测试一下
package adapter;
/**
* @ClassName: Test
* @Description: TODO
* @date 2016年5月19日 下午1:27:56
*
* @author leobert.lan
* @version 1.0
*/
public class Test {
public static void main(String[] args) {
Target apis = new Adapter();
apis.targetApi1();
apis.targetApi2();
Target apiswrapper = new Adapter2(new Adaptee());
apiswrapper.targetApi1();
apiswrapper.targetApi2();
}
}
两种实现的结果是一致的
call api1
targetApi2, do nothing,complete it for your business
call api1
targetApi2, do nothing,complete it for your business
使用对象适配器模式会更灵活一些
值得一提的是:适配器中有一种缺省适配器的实现方法,当然和以上两种不属于一个范畴,它是用来实现定制化需求的。举个例子:设计之初抽象出了一个接口类,其中有3个方法,但在某些特殊情况下,只会调用一个方法。再定义一个接口是没有太多道理的,而傻乎乎的去实现会出现很多空方法,甚至你为了避免将来产生误解,还需要额外的注释说明这些方法在某种情况下不会被调用。
这种情况下你需要缺省适配器。
定义一个抽象父类实现接口:
package adapter;
/**
* @ClassName: AbsTargetAdapter
* @Description: 缺省适配器的抽象父类
* @date 2016年5月19日 下午1:33:22
*
* @author leobert.lan
* @version 1.0
*/
public abstract class AbsTargetAdapter implements Target{
@Override
public void targetApi1() {
// TODO Auto-generated method stub
}
@Override
public void targetApi2() {
// TODO Auto-generated method stub
}
}
这样他的子类就可以灵活的根据实际情况来实现需要的功能。如果有某个方法是必须要实现,且不同场景逻辑不一样,那就不要在抽象父类中实现它。
GOF
意图:
将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适用性:
你想使用一个已经存在的类,而它的接口不符合你的需求。
你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
(仅适用于对象Adapter )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
桥接
这真的是一个复杂系统才会用到的,思来想去我删掉了此处的demo,因为我的理解很可能是错误的。
TODO:重新阅读GOF相关部分
组合
直接上demo
package composite;
/**
* @ClassName: IComponent
* @Description: TODO
* @date 2016年5月20日 下午12:59:36
*
* @author leobert.lan
* @version 1.0
*/
public interface IComponent {
void doSomething();
}
Compositer
package composite;
import java.util.ArrayList;
/**
* @ClassName: Compositer
* @Description: TODO
* @date 2016年5月20日 下午1:02:55
*
* @author leobert.lan
* @version 1.0
*/
public class Compositer implements IComponent{
private ArrayList<IComponent> tree = new ArrayList<>();
@Override
public void doSomething() {
for (IComponent leaf :tree) {
leaf.doSomething();
}
}
public void add(IComponent leaf) {
tree.add(leaf);
}
public void remove(LeafClass leaf) {
tree.remove(leaf);
}
}
leaf
package composite;
/**
* @ClassName: LeafClass
* @Description: TODO
* @date 2016年5月20日 下午1:02:24
*
* @author leobert.lan
* @version 1.0
*/
public class LeafClass implements IComponent{
@Override
public void doSomething() {
System.out.println("i am doing something");
}
}
测试一下:
package composite;
/**
* @ClassName: Test
* @Description: TODO
* @date 2016年5月20日 下午1:06:38
*
* @author leobert.lan
* @version 1.0
*/
public class Test {
public static void main(String[] args) {
Compositer compositer = new Compositer();
Compositer compositer2 = new Compositer();
Compositer compositer3 = new Compositer();
LeafClass leaf1 = new LeafClass();
LeafClass leaf2 = new LeafClass();
LeafClass leaf3 = new LeafClass();
LeafClass leaf4 = new LeafClass();
//可以很随意的组合
compositer2.add(leaf1);
compositer2.add(leaf2);
compositer3.add(leaf4);
compositer2.add(compositer3);
compositer.add(leaf3);
compositer.add(compositer2);
compositer.doSomething();
}
}
我们发现不仅能“装叶子”
意图:
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性。
适用性:
你想表示对象的部分-整体层次结构。
你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
装饰
这个就厉害了
上demo
模拟一个屏幕绘制的例子
package decorator;
/**
* @ClassName: IWindow
* @Description: TODO
* @date 2016年5月20日 下午1:42:44
*
* @author leobert.lan
* @version 1.0
*/
public interface IWindow {
void drawWindow();
//Test
void checkWindowType();
}
抽象装饰者
package decorator;
/**
* @ClassName: AbsSimpleWindowDecorator
* @Description: TODO
* @date 2016年5月20日 下午1:44:23
*
* @author leobert.lan
* @version 1.0
*/
public abstract class AbsSimpleWindowDecorator implements IWindow {
protected IWindow windowToBeDecorated;
public AbsSimpleWindowDecorator(IWindow window) {
this.windowToBeDecorated = window;
}
@Override
public void drawWindow() {
windowToBeDecorated.drawWindow();
}
@Override
public void checkWindowType() {
System.out.print("origin:");
windowToBeDecorated.checkWindowType();
}
}
实现装饰者
package decorator;
/**
* @ClassName: HorizontalWindow
* @Description: TODO
* @date 2016年5月20日 下午1:53:38
*
* @author leobert.lan
* @version 1.0
*/
public class HorizontalWindow extends AbsSimpleWindowDecorator{
public HorizontalWindow(IWindow window) {
super(window);
}
@Override
public void drawWindow() {
super.drawWindow();
drawHbar();
}
private void drawHbar() {
System.out.println("drawing hbar");
}
@Override
public void checkWindowType() {
super.checkWindowType();
System.out.print("now");
System.out.println("this is :"+getClass().getSimpleName());
}
}
package decorator;
/**
* @ClassName: VertivalWindow
* @Description: TODO
* @date 2016年5月20日 下午1:53:07
*
* @author leobert.lan
* @version 1.0
*/
public class VerticalWindow extends AbsSimpleWindowDecorator{
public VerticalWindow(IWindow window) {
super(window);
}
@Override
public void drawWindow() {
super.drawWindow();
drawVbar();
}
private void drawVbar() {
System.out.println("drawing vbar");
}
@Override
public void checkWindowType() {
super.checkWindowType();
System.out.print("now");
System.out.println("this is :"+getClass().getSimpleName());
}
}
实际的已有的类,我们的装饰对象
package decorator;
/**
* @ClassName: SimpleWindow
* @Description: TODO
* @date 2016年5月20日 下午1:43:38
*
* @author leobert.lan
* @version 1.0
*/
public class SimpleWindow implements IWindow{
@Override
public void drawWindow() {
// TODO Auto-generated method stub
System.out.println("on Drawing ...");
}
@Override
public void checkWindowType() {
System.out.println("this is :"+getClass().getSimpleName());
}
}
测试一下
package decorator;
/**
* @ClassName: Test
* @Description: TODO
* @date 2016年5月20日 下午1:56:48
*
* @author leobert.lan
* @version 1.0
*/
public class Test {
public static void main(String[] args) {
IWindow simpleWindow = new SimpleWindow();
IWindow horizentalWindow = new HorizontalWindow(simpleWindow);
IWindow verticalWindow = new VerticalWindow(simpleWindow);
simpleWindow.drawWindow();
simpleWindow.checkWindowType();
System.out.println();
horizentalWindow.drawWindow();
horizentalWindow.checkWindowType();
System.out.println();
verticalWindow.drawWindow();
verticalWindow.checkWindowType();
}
}
on Drawing …
this is :SimpleWindowon Drawing …
drawing hbar
origin:this is :SimpleWindow
nowthis is :HorizontalWindowon Drawing …
drawing vbar
origin:this is :SimpleWindow
nowthis is :VerticalWindow
意图:
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活。
适用性:
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
处理那些可以撤消的职责。
当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
外观
一个复杂的系统,可以使用外观模式提供一些简化使用的方案,当然,在熟悉内部细节的情况下,实现定制化需求可以跳过外观层
demo:
package facade;
/**
* @ClassName: Class1
* @Description: TODO
* @date 2016年5月23日 上午9:59:47
*
* @author leobert.lan
* @version 1.0
*/
public class Class1 {
public boolean doThing1() {
//...
return true;
}
}
package facade;
/**
* @ClassName: Class2
* @Description: TODO
* @date 2016年5月23日 上午9:59:56
*
* @author leobert.lan
* @version 1.0
*/
public class Class2 {
public void doThing2(int i) {
}
}
package facade;
/**
* @ClassName: Class3
* @Description: TODO
* @date 2016年5月23日 上午10:00:04
*
* @author leobert.lan
* @version 1.0
*/
public class Class3 {
public int doThing3(boolean b) {
return 1;
}
}
facade:
package facade;
/**
* @ClassName: SysFacade
* @Description: TODO
* @date 2016年5月23日 上午10:01:04
*
* @author leobert.lan
* @version 1.0
*/
public class SysFacade {
private final Class1 c1;
private final Class2 c2;
private final Class3 c3;
public SysFacade() {
c1 = new Class1();
c2 = new Class2();
c3 = new Class3();
}
public void doSomething() {
boolean b = c1.doThing1();
int i = c3.doThing3(b);
c2.doThing2(i);
}
}
测试一下:
package facade;
/**
* @ClassName: Test
* @Description: TODO
* @date 2016年5月23日 上午10:28:07
*
* @author leobert.lan
* @version 1.0
*/
public class Test {
public static void main(String[] args) {
SysFacade facade = new SysFacade();
//目的就是让你简单的使用一个较为复杂的系统,不用关心系统内部的运作
//当然,定制性的需求可以越过facade层
facade.doSomething();
}
}
没做输出
意图:
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
适用性:
当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade 可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过facade层。
客户程序与抽象类的实现部分之间存在着很大的依赖性。引入facade 将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
当你需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系。
享元
demo:
模拟一个咖啡店下单的例子
咖啡店
package flyweight;
import java.util.List;
import java.util.Vector;
/**
* @ClassName: CoffeeShop
* @Description: TODO
* @date 2016年5月23日 上午10:41:57
*
* @author leobert.lan
* @version 1.0
*/
public class CoffeeShop {
private final List<Order> orders = new Vector<Order>();
private final Menu menu = new Menu();
public void takeOrder(String flavourName, int table) {
CoffeeFlavour flavour = menu.lookup(flavourName);
Order order = new Order(table, flavour);
orders.add(order);
}
public void service() {
for (Order order : orders)
order.serve();
}
public String report() {
return "\ntotal CoffeeFlavour objects made: " + menu.totalCoffeeFlavoursMade();
}
}
咖啡口味
package flyweight;
/**
* @ClassName: CoffeeFlavour
* @Description: 咖啡口味
* @date 2016年5月23日 上午10:35:00
*
* @author leobert.lan
* @version 1.0
*/
public class CoffeeFlavour {
private final String flavour;
public CoffeeFlavour(String flavour) {
this.flavour = flavour;
}
@Override
public String toString() {
return flavour;
}
}
订单
package flyweight;
/**
* @ClassName: Order
* @Description: 订单
* @date 2016年5月23日 上午10:40:52
*
* @author leobert.lan
* @version 1.0
*/
public class Order {
private final int tableNumber;
private final CoffeeFlavour flavour;
public Order(int tableNumber, CoffeeFlavour flavor) {
this.tableNumber = tableNumber;
this.flavour = flavor;
}
public void serve() {
System.out.println("Serving " + flavour + " to table " + tableNumber);
}
}
menu
package flyweight;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @ClassName: Menu
* @Description: TODO
* @date 2016年5月23日 上午10:40:22
*
* @author leobert.lan
* @version 1.0
*/
public class Menu {
private Map<String, CoffeeFlavour> flavours = new ConcurrentHashMap<String, CoffeeFlavour>();
public CoffeeFlavour lookup(String flavorName) {
if (!flavours.containsKey(flavorName))
flavours.put(flavorName, new CoffeeFlavour(flavorName));
return flavours.get(flavorName);
}
public int totalCoffeeFlavoursMade() {
return flavours.size();
}
}
测试一下:
package flyweight;
/**
* @ClassName: Test
* @Description: TODO
* @date 2016年5月23日 上午10:42:44
*
* @author leobert.lan
* @version 1.0
*/
public class Test {
public static void main(String[] args) {
CoffeeShop shop = new CoffeeShop();
shop.takeOrder("Cappuccino", 2);
shop.takeOrder("Frappe", 1);
shop.takeOrder("Espresso", 1);
shop.takeOrder("Frappe", 897);
shop.takeOrder("Cappuccino", 97);
shop.takeOrder("Frappe", 3);
shop.takeOrder("Espresso", 3);
shop.takeOrder("Cappuccino", 3);
shop.takeOrder("Espresso", 96);
shop.takeOrder("Frappe", 552);
shop.takeOrder("Cappuccino", 121);
shop.takeOrder("Espresso", 121);
shop.service();
System.out.println(shop.report());
}
}
意图:
运用共享技术有效地支持大量细粒度的对象。
适用性:
一个应用程序使用了大量的对象。
完全由于使用大量的对象,造成很大的存储开销。
对象的大多数状态都可变为外部状态。
如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
应用程序不依赖于对象标识。由于Flyweight 对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
代理
demo演示虚代理
package proxy;
/**
* @ClassName: IImage
* @Description: TODO
* @date 2016年5月23日 上午10:52:04
*
* @author leobert.lan
* @version 1.0
*/
public interface IImage {
void display();
}
package proxy;
/**
* @ClassName: RealImage
* @Description: TODO
* @date 2016年5月23日 上午10:53:31
*
* @author leobert.lan
* @version 1.0
*/
public class RealImage implements IImage{
private String filename = null;
/**
* Constructor
* @param filename
*/
public RealImage(final String filename) {
this.filename = filename;
loadImageFromDisk();
}
/**
* Loads the image from the disk
*/
private void loadImageFromDisk() {
System.out.println("Loading " + filename);
}
/**
* Displays the image
*/
public void display() {
System.out.println("Displaying " + filename);
}
}
package proxy;
/**
* @ClassName: ImageLoader
* @Description: 代理,wikipedia上面的例子命名为proxyImage,命名上更便于理解
* @date 2016年5月23日 上午10:52:25
*
* @author leobert.lan
* @version 1.0
*/
public class ImageLoader implements IImage {
private RealImage image = null;
private String filename = null;
/**
* Constructor
* @param filename
*/
public ImageLoader(final String filename) {
this.filename = filename;
}
/**
* Displays the image
*/
public void display() {
if (image == null) {
image = new RealImage(filename);
}
image.display();
}
}
测试一下
package proxy;
/**
* @ClassName: Test
* @Description: TODO
* @date 2016年5月23日 上午10:56:53
*
* @author leobert.lan
* @version 1.0
*/
public class Test {
public static void main(String[] args) {
final IImage IMAGE1 = new ImageLoader("HiRes_10MB_Photo1");
final IImage IMAGE2 = new ImageLoader("HiRes_10MB_Photo2");
IMAGE1.display(); // loading necessary
IMAGE1.display(); // loading unnecessary
IMAGE2.display(); // loading necessary
IMAGE2.display(); // loading unnecessary
IMAGE1.display(); // loading unnecessary
}
}
Loading HiRes_10MB_Photo1
Displaying HiRes_10MB_Photo1
Displaying HiRes_10MB_Photo1
Loading HiRes_10MB_Photo2
Displaying HiRes_10MB_Photo2
Displaying HiRes_10MB_Photo2
Displaying HiRes_10MB_Photo1
意图:
为其他对象提供一种代理以控制对这个对象的访问。
适用性:
在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。下面是一 些可以使用Proxy 模式常见情况:
1) 远程代理(Remote Proxy )为一个对象在不同的地址空间提供局部代表。 NEXTSTEP[Add94] 使用NXProxy 类实现了这一目的。Coplien[Cop92] 称这种代理为“大使” (Ambassador )。
2 )虚代理(Virtual Proxy )根据需要创建开销很大的对象。在动机一节描述的ImageProxy 就是这样一种代理的例子。
3) 保护代理(Protection Proxy )控制对原始对象的访问。保护代理用于对象应该有不同 的访问权限的时候。例如,在Choices 操作系统[ CIRM93]中KemelProxies为操作系统对象提供 了访问保护。
4 )智能指引(Smart Reference )取代了简单的指针,它在访问对象时执行一些附加操作。 它的典型用途包括:
对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它(也称为SmartPointers[Ede92 ] )。
当第一次引用一个持久对象时,将它装入内存。
在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
后记
有种写完啥也没写的感觉!没啥干货就是一种蛋疼的感觉