设计模式 结构篇

装饰者、适配器、外观,代理,这几种模式在代码层面很相似,主要的区别在于对应的业务作用的区别。

组合模式 Composite Pattern

Android 里的 View 和 ViewGroup

树形结构以表示”部分-整体”的层次结构( View 可以做为 ViewGroup 的一部分)。

组合模式使得 户对 单个对象 View 和 组合对象 ViewGroup 的使用具有一致性。

适配器模式 Adapter Pattern

也叫 Wrapper 或者 Translator。

国标插座转换成英标插座的电源适配器,这个适配器模式的作用与电源适配器作用完全一样。

业务作用是将一个接口变为另一个接口。

interface MusicPlayer{
    public void play() ;   
}

interface Singer{
    public void sing();
}

class MusicPlayerAdapter implements Singer{

    MusicPlayer musicPlayer

    public MusicPlayerAdapter(MusicPlayer musicPlayer){
        this.musicPlayer = musicPlayer;
    }
    public void singSong(){
        this.musicPlayer.play();
    }
}

public class AdpaterDemo{
    public static void main(String args[]){
        MusicPlayer musicPlayer = new MusicPlayer();
        Singer musicPlayerAdapter = new MusicPlayerAdapter(musicPlayer) ;
        musicPlayerAdapter.sing() ;
    }
}

外观模式 Facade

对于复杂的调用,比如有复杂顺序要求,有许多繁琐的 api 等,或者有些 api 的调用不想暴露出来,用一个外观类作为对外的门面,在这里用对外一个函数去调用那些复杂的内部函数。

比如 Android 中的 Context,内部有很多复杂功能通过比如 startActivty、sendBroadcast、bindService 实现。

class CPU {
    public void freeze() { ... }
    public void jump(long position) { ... }
    public void execute() { ... }
}

class HardDrive {
    public byte[] read(long lba, int size) { ... }
}

class Memory {
    public void load(long position, byte[] data) { ... }
}

/* Facade */
class ComputerFacade {
    private CPU processor;
    private Memory ram;
    private HardDrive hd;

    public ComputerFacade() {
        this.processor = new CPU();
        this.ram = new Memory();
        this.hd = new HardDrive();
    }

    public void start() {
        processor.freeze();
        ram.load(BOOT_ADDRESS, hd.read(BOOT_SECTOR, SECTOR_SIZE));
        processor.jump(BOOT_ADDRESS);
        processor.execute();
    }
}

/* Client */
class You {
    public static void main(String[] args) {
        ComputerFacade computer = new ComputerFacade();
        computer.start();
    }
}

装饰者模式 Decorator

可以在运行时,动态的添加和删除某一个或者多个功能。贯彻单一职责原则的好方法。

装饰者模式很像责任链模式,区别是在责任链中有一个明确的类来处理请求,而在装饰者模式中 ,所有的装饰者都参与处理请求。

可以实现类似多继承的效果。(实际上是多层的继承)

// The interface Coffee defines the functionality of Coffee implemented by decorator
public interface Coffee {
    public double getCost(); // Returns the cost of the coffee
    public String getIngredients(); // Returns the ingredients of the coffee
}

// Extension of a simple coffee without any extra ingredients
public class SimpleCoffee implements Coffee {
    @Override
    public double getCost() {
        return 1;
    }

    @Override
    public String getIngredients() {
        return "Coffee";
    }
}

// Abstract decorator class - note that it implements Coffee interface
public abstract class CoffeeDecorator implements Coffee {
    protected final Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee c) {
        this.decoratedCoffee = c;
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost();
    }

    @Override
    public String getIngredients() {
        return decoratedCoffee.getIngredients();
    }
}

// Decorator WithMilk mixes milk into coffee.
// Note it extends CoffeeDecorator.
class WithMilk extends CoffeeDecorator {
    public WithMilk(Coffee c) {
        super(c);
    }

    @Overriding
    public double getCost() {
        return super.getCost() + 0.5;
    }

    @Overriding
    public String getIngredients() {
        return super.getIngredients() + ", Milk";
    }
}

// Decorator WithSprinkles mixes sprinkles onto coffee.
// Note it extends CoffeeDecorator.
class WithSprinkles extends CoffeeDecorator {
    public WithSprinkles(Coffee c) {
        super(c);
    }

    @Overriding
    public double getCost() {
        return super.getCost() + 0.2;
    }

    @Overriding
    public String getIngredients() {
        return super.getIngredients() + ", Sprinkles";
    }
}

public class Main {
    public static void printInfo(Coffee c) {
        System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
    }

    public static void main(String[] args) {
        Coffee c = new SimpleCoffee();
        printInfo(c);
        c = new WithMilk(c);
        printInfo(c);
        c = new WithSprinkles(c); // 类似于 c = new WithMilk(new WithSprinkles(c));
        printInfo(c);
    }
}

代理模式 Proxy Pattern

对于调用者来说,操作并没有不同,看起来是一样的。

但是真正的处理过程是通过代理人完成,代理人可以加上一些自己的操作,可以是增加操作,也可以是修改操作。

代理与装饰者的一个重要区别是:代理中可以实例化真正的操作对象。而装饰者只能包装现有的操作对象。

比如代理服务器就是修改请求,一些商品的销售代理就是把货品卖给你的同时自己也抽成。

interface Give{
    public void giveMoney() ;
}

class RealGive implements Give{
    public void giveMoney(){
        System.out.println(real give money") ;
    }
}

class GiveProxy implements Give{

    private Give give = null ;

    public GiveProxy(Give give){
        this.give = give ;
    }

    public void before(){
        System.out.println("before") ;
    }
    
    public void after(){
        System.out.println("after") ;
    }
    public void giveMoney(){
        this.before() ;
        this.give.giveMoney() ;
        this.after() ;
    }
}

public class ProxyDemo{
    public static void main(String args[]){
        Give give = new GiveProxy(new RealGive()) ;
        give.giveMoney() ;
    }
}

桥接模式 bridge

将抽象部分与实现部分分离,使他们独立地进行变化。 抽象部分和实现部分(固有操作)的区别依据具体的业务逻辑而定。

其实就是,一个类除了本身的固有操作,还存在两个或者多个维度的变化,且这多个维度都扩展的可能,使用桥接模式可以方便在多个维度进行扩展。

也可以达到类似多继承的效果。本质上是通过增加耦合,不用继承。

与装饰者模式的区别在于:装饰者模式把多出来的部分放到单独的类里面,每个单独的类都是一个单一的功能。桥接模式则把多出来的功能按照某种维度抽象出来,形成不同的接口/抽象类,然后让具体的不同功能按照他的维度去实现不同的接口。

/** "Implementor" */
interface DrawingAPI {
    public void drawCircle(int x, int y, int radius);
    public void drawDot(int x, int y);
}

/** "ConcreteImplementor"  1/2 */
class DrawingAPIWindows implements DrawingAPI {
    public void drawCircle(int x, int y, int radius) {
        System.out.printf("API Windows circle);
    }
     public void drawCircle(int x, int y) {
        System.out.printf("API Windows dot);
    }
}

/** "ConcreteImplementor" 2/2 */
class DrawingAPIUbuntu implements DrawingAPI {
    public void drawCircle(int x, int y, int radius) {
        System.out.printf("API Ubuntu circle);
    }
   public void drawCircle(int x, int y) {
        System.out.printf("API Ubuntu dot);
    }
}

/** "Abstraction" */
abstract class Shape {
    protected DrawingAPI drawingAPI;

    protected Shape(DrawingAPI drawingAPI){
        this.drawingAPI = drawingAPI;
    }

    public abstract void draw();                                 // low-level
    public abstract void resizeByPercentage(final double pct);   // high-level
}

/** "Refined Abstraction" */
class CircleShape extends Shape {
    private int x, y, radius;
    public CircleShape(int x, double y, int radius, DrawingAPI drawingAPI) {
        super(drawingAPI);
        this.x = x;  this.y = y;  this.radius = radius;
    }

    // low-level i.e. Implementation specific
    public void draw() {
        drawingAPI.drawCircle(x, y, radius);
    }
    // high-level i.e. Abstraction specific
    public void resizeByPercentage(final double pct) {
        radius *= (1.0 + pct/100.0);
    }
}

/** "Client" */
class BridgePattern {
    public static void main(final String[] args) {
        Shape shape =  new CircleShape(1, 2, 3, new DrawingAPIWindows()),
        shape.resizeByPercentage(2.5);
        shape.draw();
    }
}

享元模式 Flyweight

Flyweight,蝇量级,拳击里的一个体重级别,在 100 斤上下,基本是最轻的一个拳击级别。

所以,这个模式的目的就是轻,也就是减少内存,减少 io 等各种性能开销,具体的实现方式,就是中文译名:共享单元。

  • 可以共享的相同内容称为内部状态(Intrinsic State)
  • 需要外部环境来设置的不能共享的内容称为外部状态(Extrinsic State)
  • 由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态,使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。

有 Flyweight Factory,Flyweight 接口,Flyweight 实现类这几个角色。 通常使用某种工厂模式,使用Flyweight Factory 去生产 Flyweight。

仔细一看代码会发现,这不就是个简单的缓存嘛。

public interface SmartPhoneModel {
   void double getScreenSize();
}

public IPhone4 implements SmartPhoneModel{

    private String color ;

    public IPhone4(String color){
        this.color = color ;
    }

    void double getScreenSize(){
        return 3.5 ;
    }
}
 
public class SmartPhoneFactory {
    private static final Map<String, SmartPhone> extrinsicStateKeyCache = new HashMap<>();

    public static SmartPhoneModel getSmartPhone(String color) {
        SmartPhoneModel smartPhoneModel = extrinsicStateKeyCache.get(modelStr);

        if(smartPhoneModel == null) {
            smartPhoneModel = new IPhone4(color);
            extrinsicStateKeyCache.put(color, smartPhoneModel);
        }
        return smartPhoneModel;
    }
}

参考

https://en.wikipedia.org/wiki/Design_Patterns http://design-patterns.readthedocs.io/zh_CN/latest/index.html