设计模式学习(六) 适配器模式

引入


什么是适配器?

设计模式学习(六) 适配器模式_第1张图片

引用head first 的一幅图

它位于插头和插座中间,目的是将欧式插座转换为美式插座,从而让美式插头插进这个插座得到电力。或者可以这么理解:适配器改变了插座的接口,以符合美式笔记本电脑的需求。

在面向对象中的适配器,将一个接口转换成另一个接口,以符合客户的期望。

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

UML类图

实际使用中有两种“适配器”:对象适配器和类适配器。

对象适配器

设计模式学习(六) 适配器模式_第2张图片

  • Target:目标角色,暴露给客户的目标接口。
  • Adaptee:现在需要适配的接口;
  • Adapter:适配器角色,适配器把源接口转换成目标接口,所以这一个角色必须是具体类。

Adapter实现目标接口,持有Adaptee对象的引用,在重写request方法中,将Target目标接口与Adaptee被适配者联系起来。

这个 模式充满良好的oo设计原则:使用对象组合,以修改的接口包装被适配者:这种做法还有额外的优点,就是,被适配者的任何子类,都可以搭配着适配器使用。

类适配器

设计模式学习(六) 适配器模式_第3张图片

类适配器和对象适配器,唯一的不同就是:对象适配器是用引用的方式持有被适配对象,而类适配器继承了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();
	}

}

结果

设计模式学习(六) 适配器模式_第4张图片

火鸡没有和鸭子一样的接口,换句话说,火鸡没有quack()方法......适配器实现了鸭子的接口,但它收到方法调用的时候,全部委托给火鸡,火鸡(被适配者)全接到客户作用于鸭子接口上的调用。

Java 中的适配器

早期的集合(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基本上是一致的,也是通过一个匿名内部类来实现具体的方法。

oo基础

抽象

封装

继承

多态

oo原则

1.多用组合,少用继承

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

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

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

总结

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

2.适配器改变接口以符合客户的期望

3.适配器有两种形式:对象适配器和类适配器。类适配器需要用到多重继承

4.适配器将一个对象包装以改变其接口,装饰者将一个对象包装起来以增加新的行为。

5.外观模式是一个高频率使用的设计模式,它的精髓就在于封装二字。通过一个高层次结构为用户提供统一的 API 入口,使得用户通过一个类型就基本能够操作整个系统,这样减少了用户的使用成本,也能够提升系统的灵活性

6.外观类遵循了一个很重要设计模式原则:迪米特原则(最少知识原则),它让客户端依赖于最少的类,直接依赖外观类而不是依赖于所有的子系统类。 

引用

[1] 弗里曼. Head First 设计模式(中文版)[Z]. 中国电力出版社: O'Reilly Taiwan公司 ,2007.

源码下载

https://github.com/isheroleon/design

你可能感兴趣的:(java学习,设计模式)