Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1715915
  • 博文数量: 177
  • 博客积分: 9416
  • 博客等级: 中将
  • 技术积分: 2513
  • 用 户 组: 普通用户
  • 注册时间: 2006-01-06 16:08
文章分类

全部博文(177)

文章存档

2013年(4)

2012年(13)

2011年(9)

2010年(71)

2009年(12)

2008年(11)

2007年(32)

2006年(25)

分类:

2009-09-22 17:03:30

Firstly I'd like to cite the description in GoF DP about Abstract Factory, one of the creational patterns, because I cannot express them in much more elegant and incisive:

Definition: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

Pay attention to "families". This defines the essential difference between Abstract Factory and Factory Method. In Factory Method, only one kind of objects will be created. However, Abstract Factory will create a series of factories which will then create a set of related objects.

For example, one would like to have some data readers and writers to read data from different sources and then after necessary operations on data, write them to different destinations. This include read/write from/to databases, network, local disk, etc. Another example is multimedia player which needs also encode/decode data.

Participants
The classes and/or objects participating in this pattern are:
  • AbstractFactory
o declares an interface for operations that create abstract products
  • ConcreteFactory
o implements the operations to create concrete product objects
  • AbstractProduct
o declares an interface for a type of product object
  • Product
o defines a product object to be created by the corresponding concrete factory
o implements the AbstractProduct interface
  • Client
o uses interfaces declared by AbstractFactory and AbstractProduct classes

Below is the class diagram:


Client uses AbstractFactory to create objects, so AbstractFactory is part of the interface. AbstractProductA and AbstractProductB are part of the interface as well. Once the interface is defined, we can PROGRAM TO INTERFACE. Here we use Encoder/Decoder example (however, real world Encoder/Decoder is very complicated):

int main()
{
    cout << "========== Audio Factory ==========" << endl;
    CodecFactory* factory1 = new AudioCodecFactory();
    Client* cli = new Client();
    cli->GetEncoder(factory1);
    cli->GetDecoder(factory1);
    cli->DoEncode();
    cli->DoDecode();
    delete cli;
    delete factory1;
    cout << "========== Video Factory ==========" << endl;
    CodecFactory* factory2 = new VideoCodecFactory();
    cli = new Client();
    cli->GetEncoder(factory2);
    cli->GetDecoder(factory2);
    cli->DoEncode();
    cli->DoDecode();
    delete cli;
    delete factory2;
    return 0;
}


The behaviour must be fixed. That is, the business logic of Client will keep unchanged no matter which kind of codec will be used.

Below is the definition of interface:

class Encoder
{
    public:
    virtual void Encode() = 0;
};


class Decoder
{
    public:
    virtual void Decode() = 0;
};


class CodecFactory
{
    public:
    virtual Encoder* CreateEncoder() = 0;
    virtual Decoder* CreateDecoder() = 0;
};

Actually Encoder and Decoder are not as the same level as CodecFactory, as demostrated in class diagram, and the business logic code.

All source code is listed below:

#include <iostream>

using namespace std;

/* This is just a sample of using abstract factory pattern. */
class Encoder
{
    public:
    virtual void Encode() = 0;
};

class Decoder
{
    public:
    virtual void Decode() = 0;
};

class H263Encoder: public Encoder
{
    public:
    void Encode();
};

void H263Encoder::Encode()
{
    cout << "H263Encoder::Encode()" << endl;
}

class JPEGEncoder: public Encoder
{
    public:
    void Encode();
};

void JPEGEncoder::Encode()
{
    cout << "JPEGEncoder::Encode()" << endl;
}

class H263Decoder: public Decoder
{
    public:
    void Decode();
};

void H263Decoder::Decode()
{
    cout << "H263Decoder::Decode()" << endl;
}

class JPEGDecoder: public Decoder
{
    public:
    void Decode();
};

void JPEGDecoder::Decode()
{
    cout << "JPEGEncoder::Decode()" << endl;
}

class CodecFactory
{
    public:
    virtual Encoder* CreateEncoder() = 0;
    virtual Decoder* CreateDecoder() = 0;
};

class AudioCodecFactory: public CodecFactory
{
    public:
    Encoder* CreateEncoder();
    Decoder* CreateDecoder();
};

Encoder* AudioCodecFactory::CreateEncoder()
{
    return new H263Encoder();
}

Decoder* AudioCodecFactory::CreateDecoder()
{
    return new H263Decoder();
}

class VideoCodecFactory: public CodecFactory
{
    public:
    Encoder* CreateEncoder();
    Decoder* CreateDecoder();
};

Encoder* VideoCodecFactory::CreateEncoder()
{
    return new JPEGEncoder();
}

Decoder* VideoCodecFactory::CreateDecoder()
{
    return new JPEGDecoder();
}

class Client
{
    public:
    void GetEncoder(CodecFactory* aFactory) { iEncoder = aFactory->CreateEncoder(); }
    void GetDecoder(CodecFactory* aFactory) { iDecoder = aFactory->CreateDecoder(); }
    void DoEncode() { iEncoder->Encode(); }
    void DoDecode() { iDecoder->Decode(); }
    ~Client() { if (iEncoder != NULL) delete iEncoder; if (iDecoder != NULL) delete iDecoder; iEncoder = NULL; iDecoder = NULL; }
    private:
    Encoder* iEncoder;
    Decoder* iDecoder;
};

int main()
{
    cout << "========== Audio Factory ==========" << endl;
    CodecFactory* factory1 = new AudioCodecFactory();
    Client* cli = new Client();
    cli->GetEncoder(factory1);
    cli->GetDecoder(factory1);
    cli->DoEncode();
    cli->DoDecode();
    delete cli;
    delete factory1;
    cout << "========== Video Factory ==========" << endl;
    CodecFactory* factory2 = new VideoCodecFactory();
    cli = new Client();
    cli->GetEncoder(factory2);
    cli->GetDecoder(factory2);
    cli->DoEncode();
    cli->DoDecode();
    delete cli;
    delete factory2;
    return 0;
}

The class diagram tells us that the number of categories of product is fixed because the interface -- Abstract Factory -- cannot be changed, but more product families can be added. In our example, we can have H.263 codecs, H.264 codecs, mp3 codecs, etc., but all of them are families of encoder and decoder. That is, we can have concreteFactoryN, ProductAN and ProductBN, but we cannot have ProductC. So if this pattern is suitable for your application, you must confirm the number of products in a family when designing AbstractFactory class.

Consider we have concreteFactory1, concreteFactory2, ..., concreteFactoryN, when N becomes large, we will confuse ourselves with so many factories. So Abstract Factory is not suitable for this kind of problems.

It's easy to understand Abstract Factory pattern, but hard to determine where to use it (This rule applies to all design patterns). Keep the definition in mind, do more practice, then you will feel confortable with choosing patterns for specific problems.

Also keep in mind this: DO NOT use patterns just because you want to.

PS. You can put Client::GetEncoder and Client::GetDecoder into Client::Client(), but it's dangerous because the ctor may throw. This is topic about C++ programming language, so I choose to seperate them.
阅读(1422) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

fera2009-09-23 10:13:07

Abstract Factory focuses on creation of a set of Factory Method.