适配器模式通常用于将一个类的接口转换为客户需要的另外一个接口,通过使用Adapter模式能够使得原本接口不兼容而不能一起工作的类可以一起工作。

这里将通过分析c++的标准模板库(STL)中的适配器来学习adapter模式。由于STL中的适配器过多,不可能全部都具体介绍,所有这里将简单介绍STL中存在的适配器,但将通过具体分析STL中istream_iterator适配器来分析adapter模式。


1. STL中的适配器

在STL中广泛使用了Adapter模式,主要有container adapter、iterator adapter、functor adapter

  • container adapter: stack, queue 数据结构
  • iterator adapter: front_insert_iterator, back_insert_iterator, istream_iteator, ostream_iterator
  • functor adapter: bind, negate, compose 与对一般函数或者成员函数的修饰


2. Container Adapters

在STL中,stack、queue、priority queue是通过借助deque的所提供的接口来实现。

假设客户(希望使用stack、queue、priority queue的程序员)希望能够使用stack数据结构,则需要提供一个能够实现stack功能的类给客户,该类应该提供能够实现stack的push与pop操作的接口给客户调用,并且该类中不应该含有其他与stack能够提供的功能无关的接口。但是,当程序中不存在这样的类时,则可以通过借助已经存在的类deque,定义一个stack类,使用deque所提供的接口来实现stack类应该提供的接口,即上文所述—将一个类的接口转换为客户需要的另外一个接口。


3. Iterator Adapters

迭代器本身是一种模式,迭代器模式使得客户可以抽象地看待各种数据容器,即将所有的数据容器(vector, list, deque等)看做一个数据流。流中的每个元素为一个数据(可以为各种类型的数据),通过对迭代器执行自增与自减操作,可以遍历流中的每一个元素。这样,无论一个数据结构是如何实现的,都可以将其看做一个流,如同数组一样,通过自增自减即可获取下一个元素或者上一个元素。

类似的从抽象角度看待数据容器的观点,使我们想起Unix中将文件、套接字等都统一看做文件来进行处理的观点,文件与套接字都支持从打开、关闭、读取数据、输入数据等操作,但是具体的实现细节不同,对于熟悉面向对象的人而言,能够立即想到面向对象中的多态,接口相同而具体行为不同。

在如今,通过对文件、套接字进一步抽象,将文件、套接字等具有输入与输出功能的物体抽象为输入与输出流,如在Java中的字节流、文件流、套接字等。我们同样可以遍历输入输出流中的每个元素,从这个角度讲,从输入流中读取数据就如同从容器中读取数据一样,向输出流中输入数据则如同向容器中增加元素一样,则在STL中能够适用于容器的算法应该也需要能够使用与输入输出流。istream_iterator与ostream_iterator即是为了能够让适用于容器的算法同样也能够适配于输入输出流而出现的。


void IstreamIteratorTest( )
{
const int size = 10;
auto print = [](int x) { std::cout << x << std::endl; }; std::vector<int> vec;
for (int i = 0; i < size; ++i)
vec.push_back(i);
std::vector<int> copy_from_container(size);
std::copy(vec.begin( ), vec.end( ), copy_from_container.begin());
std::for_each(copy_from_container.begin( ), copy_from_container.end( ), print); std::vector<int> copy_from_istream(size);
std::istream_iterator<int> int_istream(std::cin), eos;
std::copy(int_istream, eos, copy_from_istream.begin());
std::for_each(copy_from_istream.begin( ), copy_from_istream.end( ), print);
}

在该程序中,copy_from_container拷贝vec容器中的元素,copy_from_istram通过从输入流中读取数据来拷贝到容器中,这里可以将容器vec和输入流都看做可以不断读取数据元素的流,通过将在流中读取的每一个元素都复制到copy_from_container或者copy_from_istream中,也可以看做读取到copy_from_container或者copy_from_istream输出流中,重点是这里使用了相同的算法copy,copy算法本来适用于容器的迭代器,通过对迭代器的自增、自减来遍历获取容器中的元素,而istream与ostream并不支持copy算法中对迭代器的取值与自增、自减等操作,通过使用Adapter模式,使能够使用与容器的算法同样也能够使用与输入与输出流,即—使用Adapter模式能够使得原本接口不兼容而不能一起工作的类可以一起工作。


// copy algorithm
template <typename InputIterator, typename OutputIterator>
inline OutputIterator copy(InputIterator first, InputIterator last,
OutputIterator result, input_iterator_tag)
{
for (; first != last; ++first, ++result)
*result = *first;
return result;
}


这里给出istream_iterator的实现:

template <typename T, typename Distance> class istream_iterator;

template <typename T, typename Distance>
bool operator==(const istream_iterator<T, Distance>& x, const istream_iterator<T, Distance>& y); template <typename T, typename Distance = ptrdiff_t>
class istream_iterator {
friend bool operator==<>(const istream_iterator<T>& x, const istream_iterator<T>& y); public:
typedef input_iterator_tag iterator_category;
typedef T value_type;
typedef const T* pointer;
typedef const T& reference;
typedef Distance difference_type; istream_iterator( ) : stream(&std::cin), end_marker(false) { }
istream_iterator(std::istream& s) : stream(&s), end_marker(false) { read( ); }
reference operator*( ) const { return value; }
pointer operator->( ) const { return &(operator*( )); }
bool operator!=(const istream_iterator<T, Distance>& rhs)
{
return !(*this == rhs);
} istream_iterator<T, Distance>& operator++( )
{
read( );
return *this;
} istream_iterator<T, Distance>& operator++(int)
{
istream_iterator<T, Distance> tmp = *this;
read( );
return tmp;
} protected:
std::istream* stream;
T value;
bool end_marker; void read( )
{
end_marker = (*stream) ? true : false;
if (end_marker)
*stream >> value;
end_marker = (*stream) ? true : false;
} bool equal(const istream_iterator& x) const
{
return (end_marker == x.end_marker) && (!end_marker || stream == x.stream);
} }; template <typename T, typename Distance>
bool operator==(const istream_iterator<T, Distance>& x, const istream_iterator<T, Distance>& y)
{
return x.equal(y);
}

front_insert_iterator、back_insert_iterator与insert_iterator的出现是因为在某些情况下需要将对迭代器的赋值操作转换为push操作或者insert操作,不同之处在于插入的元素位置不同。如copy算法是将一个迭代器所指的数据拷贝到另一个迭代器所指的位置上,而如果想在该位置插入元素,而并不想对原来的该位置上的元素值进行更改,则需要使用使用插入操作替换赋值操作,这里就可以更加插入的位置不同而选择相应的front_insert_iterator、back_insert_iterator或者insert_iterator。


4. Functor Adapters

仿函数适配器主要用来修饰函数,为函数添加某些需要的功能,在函数式语言如Scheme中可以通过高阶函数实现。在c++中可以通过将每个函数实现为一个class来模拟这种行为。如通过函数g(x) = 3 * x, f(x) = x + 2构造出函数t(x) = f(g(x)),则可以使用Functor Adaptors来实现,下面给出Scheme与c++的实现方法,Java的实现方法与c++类似。


Scheme实现:

(define (func-t func-f func-g)
(lambda (x)
(func-f (func-g x))))
(define (func-f x)
(+ x 2))
(define (func-g x)
(* x 3)) (print ((func-t func-f func-g) 3))


c++实现:

template <typename Func1, typename Func2>
class T {
private:
Func1 func1_;
Func2 func2_; public:
T(Func1 func1, Func2 func2)
: func1_(func1), func2_(func2)
{ } int operator()(int x)
{
return func1_(func2_(x));
}
}; void FunctorTest( )
{
auto func1 = [](int x) { return x + 2; };
auto func2 = [](int x) { return x * 3; };
std::cout << T<decltype(func1), decltype(func2)>(func1, func2)(3) << std::endl;
}

适配器模式—STL中的适配器模式分析的更多相关文章

  1. C++ STL中的常用容器浅谈

    STL是C/C++开发中一个非常重要的模板,而其中定义的各种容器也是非常方便我们大家使用.下面,我们就浅谈某些常用的容器.这里我们不涉及容器的基本操作之类,只是要讨论一下各个容器其各自的特点.STL中 ...

  2. STL之容器适配器queue的实现框架

    说明:本文仅供学习交流,转载请标明出处,欢迎转载! 上篇文章STL之容器适配器stack的实现框架已经介绍了STL是怎样借助基础容器实现一种经常使用的数据结构stack (栈),本文介绍下第二种STL ...

  3. STL中的容器介绍

    STL中的容器主要包括序列容器.关联容器.无序关联容器等. 一]序列容器 (1) vector vector 是数组的一种类表示,提供自动管理内存的功能,除非其他类型容器有更好满足程序的要求,否则,我 ...

  4. stl中顺序性容器,关联容器两者粗略解释

    什么是容器 首先,我们必须理解一下什么是容器,在C++ 中容器被定义为:在数据存储上,有一种对象类型,它可以持有其它对象或指向其它对像的指针,这种对象类型就叫做容器.很简单,容器就是保存其它对象的对象 ...

  5. STL中的优先级队列priority_queue

    priority_queue(queue类似)完全以底部容器为根据,再加上二叉堆(大根堆或者小根堆)的实现原理,所以其实现非常简单,缺省情况下priority_queue以vector作为底部容器.另 ...

  6. 深入解析C++ STL中的常用容器

    转载:http://blog.csdn.net/u013443618/article/details/49964299 这里我们不涉及容器的基本操作之类,只是要讨论一下各个容器其各自的特点.STL中的 ...

  7. STL中的容器

    STL中的容器 一. 种类: 标准STL序列容器:vector.string.deque和list. 标准STL关联容器:set.multiset.map和multimap. 非标准序列容器slist ...

  8. 仿函数(二、stl中常用仿函数)

    提到C++ STL,首先被人想到的是它的三大组件:Containers, Iterators, Algorithms,即容器,迭代器和算法.容器为用户提供了常用的数据结构,算法大多是独立于容器的常用的 ...

  9. 初步STL该容器适配器

    容器适配器 特点 容器一定的顺序来实现(让现有的以集装箱堆放/式工作) 分类 1) stack: 头文件 <stack> • 栈 -- 后进先出 2) queue: 头文件 <que ...

随机推荐

  1. html中的圆角边框

    border-radius:20px; radius:以某某为半径画圆. 如何制作一个圆形: div{height:150px;//像素的一半,再加上边框的像素 width:150px; border ...

  2. Python读取PDF内容

    1,引言 晚上翻看<Python网络数据采集>这本书,看到读取PDF内容的代码,想起来前几天集搜客刚刚发布了一个抓取网页pdf内容的抓取规则,这个规则能够把pdf内容当成html来做网页抓 ...

  3. td太多内容显示...

    table style="table-layout:fixed;"td style="text-overflow: ellipsis;white-space: nowra ...

  4. android XML格式颜色

    <!--android:background="@color/"-> <?xml version="1.0" encoding="u ...

  5. Delphi XE6 通过JavaScript API调用百度地图

    参考昨天的内容,有朋友还是问如何调用百度地图,也是,谁让咱都在国内呢,没办法,你懂的. 首先去申请个Key,然后看一下百度JavaScript的第一个例子:http://developer.baidu ...

  6. item Collaborative Filtering

    算法步骤: 1.计算物品相似度 2.根据用户购买记录,推荐相似物品   物品相似度定义: A.    购买i的人里面,有多少比例购买了j    缺点(推荐系统需要能挖掘长尾信息,此处若j很热门,则w趋 ...

  7. Hibernate、批量操作数据

    Hibernate 批量操作数据可以使用两种方法实现 1.分批更新,每一小批同步一次数据: public void saveEmployee2(){ Session s=HibernateSessio ...

  8. Struts2之自定义局部类型转换器、全局类型转换器

    Struts2自定义类型转换器分为局部类型转换器和全局类型转换器 (1)局部类型转换器  如果页面传来一个参数reg.action?birthday=2010-11-12到后台action,然后属性用 ...

  9. AndroidUI 引导页面的使用

    一个应用程序都少不了欢迎页面和引导页面,本文主要讲如何制作一个引页面: 首页所有的目录结构: 新建Welcome引导页面和Activity: <RelativeLayout xmlns:andr ...

  10. MySQL- InnoDB锁机制

    InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION):二是采用了行级锁.行级锁与表级锁本来就有许多不同之处,另外,事务的引入也带来了一些新问题.下面我们先介绍一点背景知识 ...