创建型模式
创建型模式
前提:为一个电脑游戏创建一个迷宫,忽略迷宫的诸多细节,仅仅关注迷宫是怎样被创建的。
定义:将迷宫定义为一系列房间(Room),房间的四面要么是一堵墙(Wall),要么是一扇门(Door)。
//房间四个面
enum Direction{ North, South, East, West};
//房间
class Room{...}
//墙
class Wall{...}
//门
class Door{...}
//迷宫
class Maze{...}
//迷宫游戏
class MazeGame{...}
一个简单的迷宫创建函数,如下:
Maze* MazeGame::CreateMaze()
{
Maze *aMaze = new Maze;
Room *r1 = new Room(1);
Room *r2 = new Room(2);
Door *dr = new Door(r1,r2);
aMaze->addRoom(r1);
aMaze->addRoom(r2);
r1->setSide(North, new Wall);
r1->setSide(South, dr);
r1->setSide(East, new Wall);
r1->setSide(West, new Wall);
r2->setSide(North, new Wall);
r2->setSide(North, new Wall);
r2->setSide(North, new Wall);
r2->setSide(North, dr);
return aMaze;
}
这个函数的缺点在于: 对迷宫布局硬编码,改变布局意味着需要改变这个函数本身,容易产生错误,且不利于重利用。试想一下,重用该迷宫的布局,但是迷宫中的部件和来原来不一致了,例如现在的门是施了魔法的,需要咒语才能开启等等。在这种情况下,改变的最大障碍就是对被实例化的类进行硬编码。
创建型模型提供了多种不同方法从实例化它们的代码中除去对这些具体类的显示引用:
- 如果CreateMaze调用虚函数而不是显式构造来创建它需要的房间、门和墙壁;那么可以通过创建一个MazeGame的子类并重定义这些虚函数,从而改变被实列化的类。正如
工厂方法模式
。 - 如果传递一个对象给CreateMaze作参数来创建房间、墙壁和门,那么可以通过传递不同的参数来改变这些部件的类。正如
抽象工厂模式
。 - 如果传递一个对象给CreateMaze,这个对象可以在它所建造的迷宫中增加房间、墙壁和门的操作,来创建一个新的迷宫。那么可以通过继承当方式来改变迷宫被建造的方式,正如
建造者模式
。 - 如果CreateMaze由多种原型的房间、墙壁和门对象参数化,它拷贝并将这些对象增加到迷宫中。正如
原型模式
。 - 单例模式可以保证每个游戏中仅有一个迷宫存在,而且所有的游戏对象都可以迅速的访问它。
1.工厂模式
工厂模式定义:
工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的接口,而无需指定它们具体的类,实例化的类是哪一个由子类决定。工厂模式将对象的实例化过程封装在一个工厂类中,使得客户端代码与具体类解耦,提供了一种灵活、可扩展的对象创建机制。
工厂模式的核心思想是将对象的创建过程封装在工厂类中,客户端代码通过与工厂类进行交互来获取所需的对象,而不需要直接实例化具体产品类。这种解耦的设计使得客户端代码更加灵活,可以通过切换具体工厂类来创建不同的产品对象。
工厂模式有以下几种常见的变体:
- 简单工厂模式(Simple Factory Pattern):使用一个单独的工厂类来创建所有的产品对象,通过
传递不同的参数给工厂方法来选择创建哪种产品
。 - 工厂方法模式(Factory Method Pattern):每个具体产品都有对应的工厂类,客户端通过调用
具体工厂类来创建具体产品对象
。 - 抽象工厂模式(Abstract Factory Pattern):提供一个抽象工厂接口,具体工厂类实现该接口并负责创建
一系列相关或相互依赖的产品对象(把一系列工厂方法归结到一起)
。
1.1 抽象工厂模式(Abstract factory)
抽象工厂结构图:
在抽象工厂模式中,通常有以下几个角色:
抽象工厂(Abstract Factory):定义了创建产品的接口,包含了创建产品的抽象方法。
具体工厂(Concrete Factory):实现了抽象工厂接口,负责实际创建具体产品的对象。
抽象产品(Abstract Product):定义了产品的接口,是具体产品类的共同父类或接口。
具体产品(Concrete Product):实现了抽象产品接口,是工厂模式所创建的对象。
用抽象工厂来实现迷宫创建,代码如下:
class MazeFactory
{
virtual Maze *MakeMaze(){return new Maze;}
virtual Wall *MakeWall(){return new Wall;}
virtual Room *MakeRoom(int n){return new Room(n);}
virtual Door *MakeDoor(Room* r1, Room* r2){return new Door(r1,r2);}
}
//建造迷宫的程序使用MazeFactory作为参数,程序便能指定创建的房间、门、墙壁类型
Maze* MazeGame::CreateMaze(MazeFactor& factory)
{
Maze *aMaze = factory.MakeMaze();
Room *r1 = factory.MakeRoom(1);
Room *r2 = factory.MakeRoom(2);
Door *dr = factory.MakeDoor(r1,r2);
aMaze->addRoom(r1);
aMaze->addRoom(r2);
r1->setSide(North, factory.MakeWall());
r1->setSide(South, dr);
r1->setSide(East, factory.MakeWall());
r1->setSide(West, factory.MakeWall()l);
r2->setSide(North, factory.MakeWall());
r2->setSide(North, factory.MakeWall());
r2->setSide(North, factory.MakeWall());
r2->setSide(North, dr);
return aMaze;
}
//创建一个MazeFactory的子类EnchanteMazeFactory,这是一个创建了施加魔法的迷宫工厂,EnchanteMazeFactory将重定义不同的成员函数并返回Room、Wall等不同的子类
class EnchanteMazeFactory : public MazeFactory
{
...
}
//创建迷宫
MazeGame game;
EnchanteMazeFactory factory;
game.CreateMaze(factory);
1.2 工厂方法模式(Factory Method)
工厂方式模式定义:
定义一个用于创建对象的接口,让子类决定实例化具体的类。Factory Method使一个类的实例化延迟到其子类中。
工厂方法结构图,如下:
适用性:
- 当一个类不知道它所必须创建的对象的类的时候。
- 当一个类希望由它的子类来指定它所创建的对象的时候。
工厂方法的一个潜在问题是它们可能为了创建适当的Product对象而迫使你创建Creator的子类,在c++种可以通过模板解决这种问题,如下:
class Creator
{
virtual Product* CreateProduct() = 0 ;
}
template <class TheProduct>
class StanderdCreator : public Creator
{
virtual Product* CreateProduct();
}
template <class TheProduct>
Product* StanderdCreator<TheProduct>::CreateProduct()
{
return new TheProduct;
}
//使用这个模板,客户只需要提供产品类
class MyProduct : public Product
{
...
}
//使用
StanderdCreator<MyProduct> mycreator;
用工厂方法模式来实现迷宫创建,代码如下:
class MazeGame()
{
Maze * CreateMaze();
//工厂方法,推迟到子类中实现
virtual Maze* makeMaze(){ return new Maze; }
virtual Room* makeRoom(int n){ return new Room(n); }
virtual Wall* makeWall(){ return new Wall; }
virtual Door* makeDoor(Room* r1, Room* r2){ return new Door(r1,r2); }
}
Maze* MazeGame::CreateMaze()
{
Maze *aMaze = makeMaze();
Room *r1 = makeRoom(1);
Room *r2 = makeRoom(2);
Door *dr = makeDoor(r1,r2);
aMaze->addRoom(r1);
aMaze->addRoom(r2);
r1->setSide(North, makeWall());
r1->setSide(South, dr);
r1->setSide(East, makeWall());
r1->setSide(West, makeWall());
r2->setSide(North, makeWall());
r2->setSide(North, makeWall());
r2->setSide(North, makeWall());
r2->setSide(North, dr);
return aMaze;
}
//不同的游戏可以创建MazeGame子类以特别指明一些迷宫的部件,MazeGame子类可以重定义所有或者部分工厂方法以指定产品中的变化,如下
class BombedMazeGame : public MazeGame
{
virtual Wall* makeWall(){ return new BombedWall; }
virtual Room* makeRoom(int n){ return new RoomWithABomb(n); }
}
1.3 简单工厂模式(Simple Factory)
简单工厂模式定义:也称为静态工厂模式,是一种创建型设计模式,它通过一个工厂类来封装对象的创建过程,根据客户端的请求返回不同的具体对象。简而言之就是在实现过程中通过指定参数来创建相应的产品。
class Creator
{
virtual Product* Create(ProductId id)
{
if(id == MINE) return new MyProduct;
if(id == YOURS) return new YoursProduct;
return 0;
}
}
//子类可以重写Product,并引入新的标识
class MyCreator : public Creator
{
virtual Product* Create(ProductId id)
{
if(id == MINE) return new YoursProduct;
if(id == YOURS) return new MyProduct;
if(id == THRIRS) return new ThierProduct;
return Creator::Create(id);
return 0;
}
}
2. 建造者模式(Builder)
建造者模式的定义:将一个复杂对象的构建与其表示(定义)分离,使得同样的构建过程可以创建不同的表示。
建造者模式的核心思想是将一个复杂对象的构建过程分解为多个简单的步骤,每个步骤由具体建造者来实现。通过指挥者将这些步骤按照一定的顺序组合起来,最终构建出一个完整的复杂对象。这样,客户端代码只需要与指挥者进行交互,而无需关心具体的构建过程和细节。
建造者模式适用于以下情况:
- 当需要创建的对象具有复杂的内部结构,且内部部件的构建顺序和组合方式不稳定或多变时。
- 当需要创建的对象的配置选项较多,构造函数参数过多且难以维护时。
- 当希望通过一种统一的方式构建不同表示的对象时。
建造者模式结构图:
用建造者模式来实现迷宫创建,代码如下:
//MazeBuilder类定义下面的接口来创建迷宫
class MazeBuilder
{
//复杂对象的构建过程分解为多个简单的步骤,每个步骤由具体建造者来实现
virtual void BuildMaze(){}
virtual void BuildRoom(int n){}
virtual void BuildDoor(int roomFrom, int rootTo){}
virtual Maze* GetMaze(){return 0;}
};
Maze* MazeGame::CreateMaze(MazeBuilder& builder)
{
builder.BuildMaze();
builder.BuildRoom(1);
builder.BuildRoom(2);
builder.BuildDoor(1,2);
return builder.GetMaze();
}
//子类StandarMazeBuilder实现了简单迷宫的创建
class StandarMazeBuilder : public MazeBuilder
{
...
}
//创建迷宫
Maze * maze;
MazeGame game;
StandarMazeBuilder builder;
maze = game.CreateMaze(builder);
3. 原型模式(Prototypes)
原型模式定义:原型模式(Prototype Pattern)是一种创建型设计模式,它通过复制现有对象来创建新的对象,而无需通过调用构造函数来创建。原型模式基于对象的克隆来创建新的对象,可以避免重复创建相似对象的开销。
原型模式的优点包括:
- 可以避免重复创建相似对象的开销,提高创建对象的效率。
- 可以通过克隆来动态地创建对象,无需依赖具体类。
- 可以在运行时动态添加和删除原型对象。
原型模式的主要缺陷:每一个Protoypes子类都必须实现Clone操作,当类已经存在时就难以新增Clone操作,或当内部包括一些不支持拷贝或由循环引用的对象时,实现克隆可能会很困难。
原型模式特别适用于以下情形:
- 创建一个对象需要消耗的资源较多,如数据读取、硬件访问等,可以通过原型复制来提高性能
- 系统需要大量相同或相似对象(例如,克隆已有对象,改变值以指定新对象)的时候,可通过复制原型的方式来减少创建对象的开销
- 对象的状态满足上述描述,并且状态需要持续变化的场景中,可以选择原型模式。
原型模式结构图,如下:
当实现原型时,可以考虑下面一些问题:
- 使用一个原型管理器。使用一个注册表存储和检索原型。在克隆一个原型前向注册表请求该原型。原型管理器是一个关联存储器,它返回一个与给定关键字相匹配的原型。
- 原型需要实现clone操作。
- 原型clone后的初始化操作。
用原型模式来实现迷宫创建(相关类声明在抽象工厂章节),代码如下:
class MazePrototypeFactory : public MazeFactory
{
MazePrototypeFactory(Maze* m, Wall* w, Room* r, Door* d)
{
_prototypeMaze = m;
_prototypeRoom = r;
_prototypeWall = w;
_prototpyeDoor = d;
}
virtual Maze *MakeMaze(){return _prototypeMaze->clone();}
virtual Wall *MakeWall(){return _prototypeWall->clone();}
virtual Room *MakeRoom(int n)
{
Room* r = _prototypeRoom->clone();
r->init(n);
return r;
}
virtual Door *MakeDoor(Room* r1, Room* r2)
{
Door* d = _prototpyeDoor->clone();
d->init(r1,r2);
return d;
}
Maze* _prototypeMaze;
Room* _prototypeRoom;
Wall* _prototypeWall;
Door* _prototpyeDoor;
}
//创建迷宫
MazeGame game;
MazePrototypeFactory factory(new Maze, new Wall, new Room, new Door);
Maze * maze = game.CreateMaze(factory);
4. 单例模式
单例模式定义:单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问该实例的入口点。
单例模式优点:
- 提供了对唯一实例的全局访问点,方便其他类使用该实例。
- 避免了重复创建对象的开销,节省了系统资源。
- 确保了对象的一致性,因为只有一个实例存在。
单例模式的两种实现:
-
懒汉模式
class Singletion; Singletion* Singletion::sing = NULL; class Singletion { public: //方式1 static Singletion *creat_singletion() { if (!sing) { lock(); if(!sing) { sing = new Singletion(); } unlock(); } return sing; } //由于双检锁失败的原因,这里提供方式2 static Singletion& create_singletion1() { static Singletion sing1; return sing1 } void task() { cout << "singtion task ..." << endl; } private: Singletion(); Singletion(Singletion&); Singletion(Singletion&&); Singletion& operator=(const Singletion&); static Singletion *sing; //添加无智能指针时删除https://www.cnblogs.com/ring1992/p/9592817.html } //单例模板https://www.cnblogs.com/sunchaothu/p/10389842.html
-
饥汉模式
class Singletion { public: Singletion* get_instance() { return sing; } void task () { //task } private: Singletion(); Singletion(Singletion&); Singletion(Singletion&&); Singletion& operator=(const Singletion&); static Singletion *sing; } Singletion* Singletion::sing = new Singletion();
5. 创建型模式之间的联系和区别
- Abstract Factory和Builder相似,两者都可以创建复杂的对象。主要的区别是builder模式着重于一步一步构造一个复杂对象,而Abstract Factory着重于多个系列的产品对象。
- Abstract Factory模式可以通过Factory Method模式或者Builder模式来实现。
- Prototype和Abstract Factory模式在某种方面是相互竞争的,但是它们也可以一起使用。Abstract Factory可以存储一个被克隆的原型集合,并且通过集合返回大量对象。
- 在一个框架系统中,用一个系统创建的抽象类对系统进行参数化(CreateMaze函数)的方式有两种:
a.生成创建对象的子类,比如工厂方法模式,这种方法的缺点在于,如果要改变产品类,就需要创建一个新的子类。
b.定义一个对象负责明确产品对象的类,并将它作为该系统的参数,比如抽象工厂、原型模式、建造者模式。抽象工厂由这个工厂对象产生多个类的对象;建造者由这个工厂对象使用一个复杂协议逐步创建一个复杂产品;原型模式由该工厂对象通过拷贝原型对象来创建产品。