什么是适配器?
引用head first 的一幅图
它位于插头和插座中间,目的是将欧式插座转换为美式插座,从而让美式插头插进这个插座得到电力。或者可以这么理解:适配器改变了插座的接口,以符合美式笔记本电脑的需求。
在面向对象中的适配器,将一个接口转换成另一个接口,以符合客户的期望。
定义:将一个类的接口,转换成客户期望的另一个接口。适配器让原本不兼容的类可以合作无闻。
实际使用中有两种“适配器”:对象适配器和类适配器。
Adapter实现目标接口,持有Adaptee对象的引用,在重写request方法中,将Target目标接口与Adaptee被适配者联系起来。
这个 模式充满良好的oo设计原则:使用对象组合,以修改的接口包装被适配者:这种做法还有额外的优点,就是,被适配者的任何子类,都可以搭配着适配器使用。
类适配器和对象适配器,唯一的不同就是:对象适配器是用引用的方式持有被适配对象,而类适配器继承了Adaptee。
对象适配器更加遵循oo设计原则中的多用组合,少用继承,并且可以使用被适配者的任何子类,更加灵活。类适配器模式有一个好处是它不需要重新实现整个被适配者的行为,毕竟类适配器模式使用的是继承的方式,当然这么做的坏处就是失去了使用组合的弹性。
package com.zpkj.project14;
//目标接口
public interface Duck {
public void quack();
public void fly();
}
package com.zpkj.project14;
/**
* 绿头鸭 嘎嘎叫 飞
*/
public class MallarDuck implements Duck {
@Override
public void quack() {
System.out.println("Quack");
}
@Override
public void fly() {
System.out.println("I'm flying");
}
}
package com.zpkj.project14;
//火鸡接口
public interface Turkey {
public void gobble();
public void fly();
}
package com.zpkj.project14;
/**
* 火鸡类 咕咕叫 短距离飞行
*/
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 distance");
}
}
package com.zpkj.project14;
/**
* 火鸡适配器
*/
public class TurkeyAdapter implements Duck{
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();
}
}
}
package com.zpkj.project14;
public class DuckTestDrive {
public static void main(String[] args) {
MallarDuck duck = new MallarDuck();
WildTurkey turkey = new WildTurkey();
TurkeyAdapter 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 say...");
testDuck(turkeyAdapter);
}
static void testDuck(Duck duck){
duck.quack();
duck.fly();
}
}
火鸡没有和鸭子一样的接口,换句话说,火鸡没有quack()方法......适配器实现了鸭子的接口,但它收到方法调用的时候,全部委托给火鸡,火鸡(被适配者)全接到客户作用于鸭子接口上的调用。
早期的集合(collection)类型(例如Vector、Stack、Hashtable),都实现了一个名为elements()的方法,该方法会返回一个Enumeration,这个Enumeration接口可以逐一走过集合内的每个元素,而无需关心它们在集合内是如何被管理的。
以Vector为例
/**
* Returns an enumeration of the components of this vector. The
* returned {@code Enumeration} object will generate all items in
* this vector. The first item generated is the item at index {@code 0},
* then the item at index {@code 1}, and so on.
*
* @return an enumeration of the components of this vector
* @see Iterator
*/
public Enumeration elements() {
return new Enumeration() {
int count = 0;
public boolean hasMoreElements() {
return count < elementCount;
}
public E nextElement() {
synchronized (Vector.this) {
if (count < elementCount) {
return (E)elementData[count++];
}
}
throw new NoSuchElementException("Vector Enumeration");
}
};
}
/*
* %W% %E%
*
* Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.util;
/**
* An object that implements the Enumeration interface generates a
* series of elements, one at a time. Successive calls to the
* nextElement
method return successive elements of the
* series.
*
* For example, to print all elements of a Vector<E> v:
*
* for (Enumeration<E> e = v.elements(); e.hasMoreElements();)
* System.out.println(e.nextElement());
*
* Methods are provided to enumerate through the elements of a
* vector, the keys of a hashtable, and the values in a hashtable.
* Enumerations are also used to specify the input streams to a
* SequenceInputStream
.
*
* NOTE: The functionality of this interface is duplicated by the Iterator
* interface. In addition, Iterator adds an optional remove operation, and
* has shorter method names. New implementations should consider using
* Iterator in preference to Enumeration.
*
* @see java.util.Iterator
* @see java.io.SequenceInputStream
* @see java.util.Enumeration#nextElement()
* @see java.util.Hashtable
* @see java.util.Hashtable#elements()
* @see java.util.Hashtable#keys()
* @see java.util.Vector
* @see java.util.Vector#elements()
*
* @author Lee Boynton
* @version %I%, %G%
* @since JDK1.0
*/
public interface Enumeration {
/**
* Tests if this enumeration contains more elements.
*
* @return true
if and only if this enumeration object
* contains at least one more element to provide;
* false
otherwise.
*/
boolean hasMoreElements();
/**
* Returns the next element of this enumeration if this enumeration
* object has at least one more element to provide.
*
* @return the next element of this enumeration.
* @exception NoSuchElementException if no more elements exist.
*/
E nextElement();
}
观察JDK源码,不难看出,Vector集合类中的elements方法,返回了一个匿名对象。因为Vector底层还是基于数组的实现,nextElement方法相当于遍历数组元素。
在新的集合类中,如arrayarraylist,linkedlist中,开始使用了Iterator接口,这个接口和枚举接口很像,可以遍历集合内的每个元素,不同的是还提供了删除元素的能力。
以arrayList为例
/*
* %W% %E%
*
* Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.util;
/**
* An iterator over a collection. Iterator takes the place of Enumeration in
* the Java collections framework. Iterators differ from enumerations in two
* ways:
* - Iterators allow the caller to remove elements from the
* underlying collection during the iteration with well-defined
* semantics.
*
- Method names have been improved.
*
*
* This interface is a member of the
*
* Java Collections Framework.
*
* @author Josh Bloch
* @version %I%, %G%
* @see Collection
* @see ListIterator
* @see Enumeration
* @since 1.2
*/
public interface Iterator {
/**
* Returns true if the iteration has more elements. (In other
* words, returns true if next would return an element
* rather than throwing an exception.)
*
* @return true if the iterator has more elements.
*/
boolean hasNext();
/**
* Returns the next element in the iteration.
*
* @return the next element in the iteration.
* @exception NoSuchElementException iteration has no more elements.
*/
E next();
/**
*
* Removes from the underlying collection the last element returned by the
* iterator (optional operation). This method can be called only once per
* call to next. The behavior of an iterator is unspecified if
* the underlying collection is modified while the iteration is in
* progress in any way other than by calling this method.
*
* @exception UnsupportedOperationException if the remove
* operation is not supported by this Iterator.
* @exception IllegalStateException if the next method has not
* yet been called, or the remove method has already
* been called after the last call to the next
* method.
*/
void remove();
}
// Iterators
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* This implementation returns a straightforward implementation of the
* iterator interface, relying on the backing list's {@code size()},
* {@code get(int)}, and {@code remove(int)} methods.
*
*
Note that the iterator returned by this method will throw an
* {@code UnsupportedOperationException} in response to its
* {@code remove} method unless the list's {@code remove(int)} method is
* overridden.
*
*
This implementation can be made to throw runtime exceptions in the
* face of concurrent modification, as described in the specification
* for the (protected) {@code modCount} field.
*
* @return an iterator over the elements in this list in proper sequence
*
* @see #modCount
*/
public Iterator iterator() {
return new Itr();
}
private class Itr implements Iterator {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
观察源码发现,Iterator实现其实和Enumeration基本上是一致的,也是通过一个匿名内部类来实现具体的方法。
抽象
封装
继承
多态
1.多用组合,少用继承
2.针对接口编程,不针对实现编程
3.为交互对象之间的松耦合设计而努力
4.类应该对扩展开放,对修改关闭
1.当需要一个现有类,而其接口并不符合你的需要时,就使用适配器
2.适配器改变接口以符合客户的期望
3.适配器有两种形式:对象适配器和类适配器。类适配器需要用到多重继承
4.适配器将一个对象包装以改变其接口,装饰者将一个对象包装起来以增加新的行为。
5.外观模式是一个高频率使用的设计模式,它的精髓就在于封装二字。通过一个高层次结构为用户提供统一的 API 入口,使得用户通过一个类型就基本能够操作整个系统,这样减少了用户的使用成本,也能够提升系统的灵活性
6.外观类遵循了一个很重要设计模式原则:迪米特原则(最少知识原则),它让客户端依赖于最少的类,直接依赖外观类而不是依赖于所有的子系统类。
[1] 弗里曼. Head First 设计模式(中文版)[Z]. 中国电力出版社: O'Reilly Taiwan公司 ,2007.
https://github.com/isheroleon/design