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

  • 适配器模式与外观模式

定义

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

形象的说,如果你在国内使用一台美版的笔记本,那么你必然需要一个交流电适配器,适配器的作用就是将一种接口转换成另一种接口,以符合客户的期望。

这个模式可以通过创建适配器进行接口转换,让不兼容的接口变成兼容。这可以让客户从实现的接口解耦。


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

优缺点

优点:被适配者的任何子类,都可以搭配着适配器使用

应用场景

 

例子

假设以有偿一个软件系统,你希望它能和一个新的厂商类库搭配使用,但是这个新的厂商所设计出来的接口,不同于旧厂商的接口;因此我们需要一个适配器类将新厂商的接口转换成你所期望的接口。

鸭子适配器:将火鸡适配为鸭子

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 WindTurkey implements Turkey {

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

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

鸭子适配器(适配器)

public class TurkeyAdapter implements Duck {
	private Turkey turkey;
	public TurkeyAdapter(Turkey turkey) {
		this.turkey = turkey;
	}
	
	@Override
	public void quack() {
		turkey.gobble();
	}

	@Override
	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();
		
		WindTurkey turkey = new WindTurkey();
		Duck turkeyAdapter = new TurkeyAdapter(turkey);
		
		System.out.println("The Turkey says ...");
		turkey.gobble();
		turkey.fly();
		
		System.out.println("The Duck says ...");
		testDuck(duck);
		
		System.out.println("The TurkeyAdapter says ...");
		testDuck(turkeyAdapter);
	}

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

测试结果 

实际上有两种适配器:“对象”适配器和“类”适配器,前面是“对象”适配器;“类”适配器在Java中是不可能的,因为他需要使用多重继承才能实现。


真实案例:

Java早期的集合(collection)类型(如:Vector、Stack、Hashtable)都实现了一个名为elements()的方法。该方法会返回一个Enumeration(枚举)。这个Enumeration接口可以逐一走过此集合内的每一个元素,而无需知道他们在集合内是如何被管理的。

序号方法描述
0boolean hasMoreElements( )
测试此枚举是否包含更多的元素。
1Object nextElement( )
如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。

当Sun推出更新后的集合类时,开始使用了Iterator(迭代器)接口,这个接口和枚举接口很像,都可以让你遍历此集合类型内的每个元素,但不同的是,迭代器还提供了删除元素的能力。

序号方法描述
0boolean hasNext( )
测试此迭代集合是否包含下一个元素。
1Object next( )
返回集合中当前游标右侧的元素
2void remove( )
删除集合中当前游标左侧的元素

如今我们希望在新的代码中只使用迭代器,所以我们需要塑造一个适配器。

适配器的hasNext( )next( )方法很容易实现,对于remove( )方法,因为枚举是一个“只读”接口,所以我们需要把remove( )方法定义成会抛出UnsupportedOperationExeception。

public class EnumerationIterator<T> implements Iterator<T> {
	Enumeration<T> enumeration;
	
	public EnumerationIterator(Enumeration<T> enumeration) {
		this.enumeration = enumeration;
	}

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

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

外观模式就是在创建一个接口简化而统一的类,用来包装系统中一个或多个复杂的类。外观模式相当直接,很容易理解,此处就不再赘述。

 

对比

序号模式意图
0装饰者不改变接口,但加入责任
1适配器将一个接口转成另一个接口
2外观让接口更简单

 

设计原则

封装变化

多用组合,少用继承

针对接口编程,不针对实现编程

为交互对象之间的松耦合设计而努力

类应该对扩展开放,对修改关闭

依赖抽象,不依赖具体类

最少知识原则:只和朋友交谈 

总结

 当需要使用一个现有类而其接口并不符合你的需要时,就使用适配器;适配器改变接口以符合客户的期望。

当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观;外观将客户从一个复杂的子系统中解耦。

实现一个外观,需要将子系统组合进外观中,然后将工作委托给子系统执行。

小知识

 最少知识原则(墨忒尔法则Law of Demeter):只和你的密友谈话。

这个原则不希望过多的类耦合在一起,否则会造成系统太复杂而不容易被维护。

在一个对象的方法内,我们只应该调用属于以下范围的方法:

该对象本身;

被当做方法的参数而传递进来的对象;

此方法所创建或实例化的任何对象;(前三个方针告诉我们,如果某对象是调用其他的方法的返回结果,不需要调用该对象的方法)

对象的任何组件。(把“组件”想象成是被实例变量所引用的任何对象,换句话说,把这想象成是“有一个”(HAS-A)关系)

比方说:

不建议使用这个原则

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

采用这个原则

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

优点:减少对象依赖,减少维护成本。

缺点:会导致更多的“包装”类被制造出来,以处理和其他组件的沟通,可能会导致复杂度和开发时间的增加,并降低运行时的性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值