`
jgsj
  • 浏览: 1028240 次
文章分类
社区版块
存档分类
最新评论

设计模式笔记07-适配器模式与外观模式

 
阅读更多

设计模式笔记07-适配器模式与外观模式


1 引言


在本章,我们将要进行一项任务,其不可能的程度,简直就像是将一个方块放进一个圆洞中。听起来不可能?有了设计模式,就有可能。还记得装饰者模式吗?我们将对象包装起来,赋予他们新的职责。而现在则是以不同目的,包装某些对象:让他们的接口看起来不像自己而像是别的东西。为何要这样做?因为这样就可以在设计中,将类的接口转换成想要的接口,以便实现不同的接口。不仅如此,我们还要探讨另一个模式,将对象包装起来以简化其接口。

2 正文


2.1 我们周围的适配器


比如某些交流电适配器,改变插座的形状来匹配你的插头,直接把电流传过去。
那么面向对象的适配器又是什么?其实,OO适配器和真实世界的适配器扮演着同样的角色:将一个接口转换成另一个接口,以符合客户的期望。

2.2 火鸡转换器


让我们来看看使用中的适配器。还记得第一章的鸭子吧?让我们看看鸭子接口和类的一个稍微简化的版本:

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

绿头鸭是鸭子的子类

public class MallardDuck implements Duck {
	//很简单的实现:只是打印出鸭子在干什么
	public void quack() {
		System.out.println("Quack");
	}
 
	public void fly() {
		System.out.println("I'm flying");
	}
}

为您介绍最新的“街头玩禽“:火鸡

public interface Turkey {
	//火鸡不会呱呱叫,只会咕咕(gobble)叫
	public void gobble();
	//火鸡会飞,虽然飞不远
	public void fly();
}

下面是火鸡的一个具体实现

public class WildTurkey implements Turkey {
	//简单打印火鸡的动作说明
	public void gobble() {
		System.out.println("Gobble gobble");
	}
 
	public void fly() {
		System.out.println("I'm flying a short distance");
	}
}

现在,假设你缺鸭子对象,想用一些火鸡对象来冒充。显而易见,因为火鸡的接口不同,所以我们不能公然拿来用。
那么就写个适配器吧。

public class TurkeyAdapter implements Duck {
	//首先,你需要实现转成的类型接口,也就是你的客户所期望看到的接口
	//接着,需要取得要适配的对象引用,这里我们利用构造器取得这个引用
	Turkey turkey;
 
	public TurkeyAdapter(Turkey turkey) {
		this.turkey = turkey;
	}
    
	//现在我们需要实现接口中的方法。
	//quack()在类之间转换很简单,只要调用gobble()就可以了
	public void quack() {
		turkey.gobble();
	}
  
	/*
	 * 固然两个接口都具备了fly()方法,火鸡的飞行距离很短,不像鸭子可以长途飞翔。
	 * 要让鸭子的飞行和火鸡的飞行能够对应,必须连续五次调用火鸡的fly()方法来完成。
	 * @see headfirst.adapter.ducks.Duck#fly()
	 */
	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);
	}
 
	static void testDuck(Duck duck) {
		duck.quack();
		duck.fly();
	}
}

适配器模式解析:
1、客户通过目标接口调用适配器方法对适配器发出请求;
2、适配器使用被适配者接口把请求转换成被适配者的一个或者多个调用接口;
3、客户接口到调用的结果,但并未察觉这一切是适配器在起转换作用。

问:一个适配器需要做多少”适配“的工作?如果我需要实现很大的目标接口,似乎有很多工作要做。
答:的确是如此。实现一个适配器所需要进行的工作的确和目标接口的大小成正比。如果不用适配器,你就必须改写客户端的代码来调用这个新的接口,将会花许多力气来做大量的调查工作和代码改写工作。相比之下,提供一个适配器类,将所有的改变封装在一个类中,是比较好的做法。

2.3 定义适配器模式


适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
这个适配器模式充满着良好的OO设计原则:使用对象组合,以修改的接口包装被适配者。这种做法还有额外的优点,那就是,被适配者的任何子类,都可以搭配着适配器使用。
也请留意,这个模式是如何把客户和接口绑定起来,而不是和实现绑定起来的。

2.4 对象和类的适配器


现在,尽管已经定义适配器模式,但其实我们还没有告诉你有关的一切。实际上有两种适配器:”对象“适配器和”类“适配器。本章涵盖了对象适配器和类适配器。

究竟什么是”类“适配器?为什么我们还没告诉你这种适配器?因为你需要多重继承才能够实现它,这在Java中是不可能的。但是当你在使用多重继承语言的时候,还是可能遇到这样的需求。
对象适配器和类适配器使用两种不同的适配方法,分别是组合(实现接口)与继承。

2.5 真实世界的适配器


旧世界的枚举器:如果你已经使用过Java,可能记得早起的集合(collection)类型,例如Vector、Stack、HashTable,都实现了一个名为elements()的方法,该方法会返回一个Enumeration。这个Enumeration接口可以逐一走过此集合内的每个元素,而无需知道它们在集合内是如何被管理的。

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

将枚举器适配到迭代器:

public class EnumerationIterator implements Iterator {
	/*
	 * 因为我们将枚举适配成迭代器,适配器需要实现迭代器接口,适配器必须看起来就像是一个迭代器
	 * 我们利用组合的方式,将枚举结合进入适配器中,所以用一个实例变量记录枚举
	 */
	Enumeration enumeration;
 
	public EnumerationIterator(Enumeration enumeration) {
		this.enumeration = enumeration;
	}
 
	//迭代器的hasNext()方法其实是委托给枚举的hasMoreElements()方法
	public boolean hasNext() {
		return enumeration.hasMoreElements();
	}
 
	//next()委托给nextElements()方法
	public Object next() {
		return enumeration.nextElement();
	}
 
	//很不幸,我们不能支持迭代器的remove()方法,所以必须放弃,我们的做法是抛出一个异常
	public void remove() {
		throw new UnsupportedOperationException();
	}
}

2.6 外观模式(Facade Pattern)


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

构造家庭影院外观,代码比较多就不贴了,类的主要功能是将子系统的实例引入家庭影院HomeTheaterFacade。

实现简化的接口

	public void watchMovie(String movie) {
		/*
		 * watchMovie()将我们之前手动进行的每项任务依次处理。请注意,每项任务都是
		 * 委托子系统中相应的组件处理的。
		 */
		System.out.println("Get ready to watch a movie...");
		popper.on();
		popper.pop();
		lights.dim(10);
		screen.down();
		projector.on();
		projector.wideScreenMode();
		amp.on();
		amp.setDvd(dvd);
		amp.setSurroundSound();
		amp.setVolume(5);
		dvd.on();
		dvd.play(movie);
	}

外观模式:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
最少知识原则:只和你最亲密的朋友谈话。
当你正在设计一个系统时,不管是任何对象,你都要注意它所交互的类有哪些,并注意它和这些类是如何交互的。
这个原则希望我们在设计中,不要让太多的类耦合在一起,免得修改系统中一部分,会影响到其他部分。如果许多类之间相互依赖,那么这个系统就会变成一个易碎的系统,它需要花很多成本维护,也会因为太复杂而不容易被其他人了解。

究竟要怎么样才能避免这样呢?这个原则提供了一些方针:就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:
1、该对象本身
2、被当做方法(包括构造器)的参数而传递进来的对象
3、此方法所创建或实例化的任何对象
4、对象的任何组件

这听起来有点严厉,不是吗?如果调用从另一个调用中返回的对象的方法,会有什么害处呢?如果我们这样做,相当于向另一个对象的子部分发请求(而增加我们直接认识的对象数目)。在这种情况下,原则要我们改为要求该对象为我们做出请求,这么一来,我们就不需要认识该对象的组件。

3 本章小结


这两个模式在我们编码过程中其实已经有意无意的用到过,比如xml转换成json,中间会有很多对象方法,最后封装之后只有一个xmlToJson()方法就搞定了,这个就是外观模式,再比如serverlet里面的http类,通过将二进制流转换成文本流,方便我们直接获取其中的文本信息。
先行者总结,学好设计模式不一定能写出好看的代码,不学设计模式不一定写不出好看的代码。设计模式其实是贯穿整个编码过程中,有些模式潜移默化的影响着我们的编码习惯,多总结,多实践,在通往架构师的道路上努力前进。









分享到:
评论

相关推荐

    《C++20设计模式》学习笔记-第6章适配器模式学习代码

    《C++20设计模式》学习笔记-第6章适配器模式学习代码

    适配器设计模式ppt

    适配器设计模式 适配器设计模式是一种结构型设计模式,用于解决接口不兼容的问题。它允许两个原本不能一起工作的对象 совместно工作。 在现实生活中,我们经常遇到适配器的使用,例如欧式插座和笔记本...

    尚硅谷设计模式源码笔记课件.zip

    结构型模式:适配器模式(3种实现)、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式(3种实现)。行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、...

    Java 版设计模式学习笔记-java-design-patterns.zip

    这个“Java版设计模式学习笔记”涵盖了多种设计模式,旨在帮助开发者更好地理解和应用这些模式。让我们深入探讨一下其中可能包含的关键知识点。 一、单例模式 单例模式确保一个类只有一个实例,并提供一个全局访问...

    设计模式笔记

    设计模式是软件工程中的一种重要概念,它代表了在特定情境下解决问题的...设计模式笔记中的内容应该涵盖了以上所述的各种模式,通过深入学习和实践,你可以将这些模式应用到实际项目中,提升自己的编程技能和设计能力。

    设计模式整理代码-pattern.zip

    在IT行业中,设计模式是软件开发中的一种标准解决方案,它代表了在特定上下文中解决常见问题的最佳实践。这里我们关注的是一个名为"pattern.zip"的压缩包文件,它包含了23种经典的设计模式,这些模式在实践中被广泛...

    23个设计模式图解--学习笔记

    在《23个设计模式图解--学习笔记》中,我们探讨了这些模式,以便于理解和应用到实际开发中。以下是这23个设计模式的详细说明: 1. **工厂方法**(Factory Method):定义一个用于创建对象的接口,让子类决定实例化...

    适配器模式笔记1

    【适配器模式笔记1】 适配器设计模式是一种结构型设计模式,它允许不同的类协同工作,即使它们的接口不兼容。适配器模式的关键在于将一个类的接口转换成客户端期望的另一种接口,从而使得原本由于接口不兼容而不能...

    根据《JAVA与设计模式》整理的笔记及示例代码

    这份"根据《JAVA与设计模式》整理的笔记及示例代码"涵盖了Java语言和设计模式的核心概念,旨在帮助开发者理解和应用这些模式。 一、设计模式的基本概念 设计模式是对在特定情境下软件设计问题的解决方案的一种描述...

    Head First设计模式读书笔记-DesignPatterns.zip

    6. **适配器模式**:结构型设计模式,允许两个不兼容的接口之间进行通信。比如,将老版本API与新版本API进行适配,或者在不同编程语言之间进行数据交换。 7. **策略模式**:行为型设计模式,定义一系列算法,并将每...

    23种设计模式---大牛笔记[汇编].pdf

    结构型中的适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式和享元模式;以及行为型中的策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式...

    设计模式笔记(精版)

    以上只是设计模式笔记中的一部分内容,实际上每个模式都有其特定的适用场景和优缺点。理解并熟练运用这些模式,可以提高代码的可读性、可维护性和复用性,降低系统复杂度,提升软件设计的质量。在实际项目中,应根据...

    设计模式学习笔记大全

    以上就是压缩包中的设计模式学习笔记涉及到的主要内容。通过对这些模式的理解和应用,开发者可以更好地解决软件设计中的问题,提升软件的质量和可维护性。每种模式都有其适用场景,理解其背后的意图和应用场景是关键...

    行业文档-设计装置-笔记本电脑电源适配器智能控制电路.zip

    笔记本电脑电源适配器是连接笔记本电脑与交流电源的关键设备,它不仅负责转换电压,还将电能高效地传输到电脑内部。智能控制电路是电源适配器中的核心技术,旨在提高能效、保护设备并实现各种功能。下面我们将深入...

    设计模式_适配器模式.zip

    这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。 这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入...

    适配器模式

    这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。 这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入...

    HeadFirst设计模式学习笔记

    《HeadFirst设计模式学习笔记》是一份详尽的资料,旨在帮助读者深入理解并掌握设计模式这一编程领域的核心概念。设计模式是软件工程中的一种最佳实践,它在解决常见问题时提供了一种标准的解决方案,使得代码更易于...

    设计模式Golang实现《研磨设计模式》读书笔记.zip

    设计模式Golang实现《研磨设计模式》读书笔记Go语言设计模式Go语言设计模式的实例代码创建模式工厂简单模式(Simple Factory)工厂方法模式(工厂方法)抽象工厂模式(Abstract Factory)创建者模式(Builder)原型...

    设计模式之美—学习笔记

    在这个“设计模式之美”的学习笔记中,我们将探讨一些主要的设计模式,以及它们在实际开发中的应用。 首先,我们从创建型模式开始。这类模式主要用于对象的创建,如单例模式(Singleton)、工厂模式(Factory ...

    设计模式读书笔记

    设计模式是软件开发中的一种最佳实践,用于解决常见的设计问题并提供可重用的解决方案。在本文档中,我们主要探讨了设计模式的基本概念和分类,以及“状态机模式”这一特定的设计模式。 首先,设计模式可以分为三大...

Global site tag (gtag.js) - Google Analytics