设计模式之适配器模式与外观模式

Posted 我不吃饼干呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之适配器模式与外观模式相关的知识,希望对你有一定的参考价值。

适配器模式

适配器模式就是将一个接口转化成另一个接口。

一个常见的例子是插头转换器

我们知道英式插头长这样:

而国内的插头一般是这样的

这时候,你就需要一个转换插头了

 

而适配器的工作就像是一个转换插头。你不需要从新买一个电器,也不用换一个插孔,只需要一个转换器即可。

 

假设已有一个软件系统,你希望它能和新的厂商类库搭配使用,但是这个新厂商设计出来的接口,不同于旧厂商的接口。

你不想改变现有的代码,也不能改变厂商的代码,于是你可以写一个类,将新厂商接口转成你所希望的接口。

情景:

首先你有一个鸭子接口(这书有毒啊啊啊这都什么破例子)

public interface Duck {
    public void quack();
    public void fly();
}

有一个鸭子的子类

// 绿头鸭
public class MallardDuck implements Duck {

    @Override
    public void quack() {
        System.out.println("Quack");
    }

    @Override
    public void fly() {
        System.out.println("I\'m flying");
    }
    
}

然后现在有一只火鸡

public interface Turkey {
    public void gobble();
    public void fly();
}

 

public class WildTurkey implements Turkey {

    @Override
    public void gobble() {
        System.out.println("Gobble gobble");
    }

    @Override
    public void fly() {
        System.out.println("I\'m flying a short diatance");
    }
    
}

 

因为你想要鸭子,鸭子不够用了,但是我们有火鸡,于是你决定为火鸡写一个配适器,偷偷地假装它们是鸭子。。。。

当需要鸭子叫的时候,就让火鸡叫,当需要鸭子飞的时候,就让火鸡飞。火鸡飞的距离短,所以还要多飞几次。

public class TurkeyAdapter implements Duck {
    Turkey turkey;
    
    public TurkeyAdapter(Turkey turkey) {
        this.turkey = turkey;
    }
    
    public void quack() {
        turkey.gobble();
    }
    
    public void fly() {
        // 火鸡飞行距离短  要连续飞五次才能对应鸭子的飞行
        for (int i = 0; i < 5; ++i) {
            turkey.fly();
        }
    }
    
}

然后测试一下:

public class DuckTestDrive {
    public static void main(String[] args) {
        MallardDuck duck = new MallardDuck();
        
        WildTurkey turkey = new WildTurkey();
        Duck turkeyAdapter = new TurkeyAdapter(turkey);
        
        System.out.println("The turkey says...");
        turkey.gobble();
        turkey.fly();
        
        System.out.println("\\nThe Duck says...");
        testDuck(duck);
        
        System.out.println("\\nThe TurkeyAdapter says...");
        testDuck(turkeyAdapter);
    }

    private static void testDuck(Duck duck) {
        duck.quack();
        duck.fly();
    }
}

输出:

The turkey says...
Gobble gobble
I\'m flying a short diatance

The Duck says...
Quack
I\'m flying

The TurkeyAdapter says...
Gobble gobble
I\'m flying a short diatance
I\'m flying a short diatance
I\'m flying a short diatance
I\'m flying a short diatance
I\'m flying a short diatance

我们就可以把火鸡当成鸭子了~(真·有毒。。。。

 

适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

类图:

上面的类图表现的是“对象”适配器,还有一种适配器,被称作“类”适配器,需要多重继承才能实现,所以在Java中是不可能的。

现实世界一个简单的适配器。

在早期的集合实现如Vector,Stack,HashTable都实现了一个elements()的方法,该方法会返回一个Enumeration,这个接口可以逐一走过集合内的每一个元素。

而新的集合类,使用Iterator来遍历集合。

现在,将枚举适配到迭代器。

import java.util.Enumeration;
import java.util.Iterator;

public class EnumerationIterator implements Iterator {

    Enumeration enu;
    
    public EnumerationIterator(Enumeration enu) {
        this.enu = enu;
    }
    
    @Override
    public boolean hasNext() {
        return enu.hasMoreElements();
    }

    @Override
    public Object next() {
        return enu.nextElement();
    }
    
    public void remove() {
        throw new UnsupportedOperationException();
    }

}

反过来也可以将迭代器适配到枚举

import java.util.Enumeration;
import java.util.Iterator;

public class IteratorEnumeration implements Enumeration {
    Iterator iterator;
    
    public IteratorEnumeration(Iterator iterator) {
        this.iterator = iterator;
    }

    @Override
    public boolean hasMoreElements() {
        return iterator.hasNext();
    }

    @Override
    public Object nextElement() {
        return iterator.next();
    }
}

测试一下:

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;

public class ArrayListEnumTest {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("适");
        list.add("配");
        list.add("器");
        list.add("模");
        list.add("式");
        Iterator iterator = list.iterator();
        Enumeration ei = new IteratorEnumeration(iterator);
        showListByEnum(ei);
    }
    
    // 一些遗留的“客户代码”仍然使用依赖于枚举接口
    public static void showListByEnum(Enumeration enumeration) {
        while (enumeration.hasMoreElements()) {
            System.out.print(enumeration.nextElement());
        }
    }
}

 

 

外观模式

情景:当你需要看一场电影的时候,你可能需要调灯光放屏幕打开投影机选择模式……每一次都是一样的步骤,等结束的时候,又是一系列繁琐的动作。

如果提供一个简单的类,能够一下就完成这一系列工作就好了。

这时就需要用外观模式,将一系列子类包装起来,提供一个简单的接口,来替代访问一系列子系统的接口。

  • 外观模式不只简化了接口,也将客户从组件的子系统中解耦。
  • 外观模式和适配器模式可以包装许多类,但外观的意图是简化接口,而适配器的意图是将接口转换成不同接口。

 

原谅自己只能写一个拙劣的例子,折叠掉。。。

package com.wenr.chapter7;

public class FacadeTest {
    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        C c = new C();
        Facade facade = new Facade(a, b, c);
        facade.ABC1();
        System.out.println();
        facade.ABC2();
    }
}


class A {
    public void a1() {
        System.out.println("a1");
    }
    public void a2() {
        System.out.println("a2");
    }
}

class B {
    public void b1() {
        System.out.println("b1");
    }
    public void b2() {
        System.out.println("b2");
    }
}

class C {
    public void c1() {
        System.out.println("c1");
    }
    public void c2() {
        System.out.println("c2");
    }
}

class Facade {
    A a;
    B b;
    C c;
    public Facade(A a, B b, C c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
    public void ABC1() {
        a.a1();
        b.b1();
        c.c1();
    }
    public void ABC2() {
        a.a2();
        b.b2();
        c.c2();
    }
}
View Code

 

外观模式:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

类图:

设计原则:最少知识原则(又称作墨忒耳法则,Law of Demeter)

只和你的密友交谈。

这个原则希望我们在设计中,不要让太多的类耦合在一起,免得修改系统中的一部分,会影响到其他部分。

怎么才能避免这样?这个原则提供了一些方针:就任何对象而言,在该对象的方法内,我们只应调用属于以下范围的方法:

  • 该对象本身
  • 被当做方法的参数而传递进来的对象
  • 此方法所创建或实例化的任何对象
  • 对象的任何组件

举个栗子:

不使用这个原则:

public float getTemp() {
    Thermometer thermometer = station.getThermometer();
    return thermometer.getTemperature();    
}

使用这个原则:

public float getTemp() {
    return station.getTemperature();
}

应用此原则时,在气象站中加一个方法,用来向温度计请求温度。这可以减少我们所依赖的类的数目。

 

综上:

当需要使用一个现有的类而其接口并不符合你的需求时,就使用适配器。

当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观。

 

以上是关于设计模式之适配器模式与外观模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之适配器模式与外观模式

《Head First 设计模式》之适配器模式与外观模式

设计模式之适配器模式与外观模式

Head First 设计模式之适配器模式与外观模式

设计模式之代理模式适配器模式和外观模式

Head First设计模式之外观模式