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

版权声明:有抱负的小狮子 https://blog.csdn.net/weixin_38087538/article/details/82759616

引入


什么是适配器?

引用head first 的一幅图

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

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

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

UML类图

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

对象适配器

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

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()方法......适配器实现了鸭子的接口,但它收到方法调用的时候,全部委托给火鸡,火鸡(被适配者)全接到客户作用于鸭子接口上的调用。

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<E> elements() {
	return new Enumeration<E>() {
	    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
 * <code>nextElement</code> method return successive elements of the
 * series.
 * <p>
 * For example, to print all elements of a <tt>Vector&lt;E&gt;</tt> <i>v</i>:
 * <pre>
 *   for (Enumeration&lt;E&gt; e = v.elements(); e.hasMoreElements();)
 *       System.out.println(e.nextElement());</pre>
 * <p>
 * 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
 * <code>SequenceInputStream</code>.
 * <p>
 * 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<E> {
    /**
     * Tests if this enumeration contains more elements.
     *
     * @return  <code>true</code> if and only if this enumeration object
     *           contains at least one more element to provide;
     *          <code>false</code> 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: <ul>
 *	<li> Iterators allow the caller to remove elements from the
 *	     underlying collection during the iteration with well-defined
 * 	     semantics.
 *	<li> Method names have been improved.
 * </ul><p>
 *
 * This interface is a member of the 
 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
 * Java Collections Framework</a>.
 *
 * @author  Josh Bloch
 * @version %I%, %G%
 * @see Collection
 * @see ListIterator
 * @see Enumeration
 * @since 1.2
 */
public interface Iterator<E> {
    /**
     * Returns <tt>true</tt> if the iteration has more elements. (In other
     * words, returns <tt>true</tt> if <tt>next</tt> would return an element
     * rather than throwing an exception.)
     *
     * @return <tt>true</tt> 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 <tt>next</tt>.  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 <tt>remove</tt>
     *		  operation is not supported by this Iterator.
     
     * @exception IllegalStateException if the <tt>next</tt> method has not
     *		  yet been called, or the <tt>remove</tt> method has already
     *		  been called after the last call to the <tt>next</tt>
     *		  method.
     */
    void remove();
}
  // Iterators

    /**
     * Returns an iterator over the elements in this list in proper sequence.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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<E> iterator() {
	return new Itr();
    }
private class Itr implements Iterator<E> {
	/**
	 * 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

猜你喜欢

转载自blog.csdn.net/weixin_38087538/article/details/82759616