跳转到内容

C++ 编程/代码/设计模式/结构型模式

来自维基教科书,开放世界中的开放书籍

结构型模式

[编辑 | 编辑源代码]

适配器

[编辑 | 编辑源代码]

将一个类的接口转换成客户端所期望的另一个接口。适配器使原本因接口不兼容而无法一起工作的类可以协同工作。

#include <iostream>

class Dog {  // Abstract Target
public:
	virtual ~Dog() = default;
	virtual void performsConversion() const = 0;
};

class DogFemale : public Dog {  // Concrete Target
public:
	virtual void performsConversion() const override { std::cout << "Dog female performs conversion." << std::endl; }
};

class Cat {  // Abstract Adaptee
public:
	virtual ~Cat() = default;
	virtual void performsConversion() const = 0;
};

class CatFemale : public Cat {  // Concrete Adaptee
public:
	virtual void performsConversion() const override { std::cout << "Cat female performs conversion." << std::endl; }
};

class DogNature {
public:
	void carryOutNature(Dog* dog) {
		std::cout << "On with the Dog nature!" << std::endl;
		dog->performsConversion();
	}
};

class ConversionAdapter : public Dog {  // Adapter
private:
	Cat* cat;
public:
	ConversionAdapter(Cat* c) : cat(c) {}
	virtual void performsConversion() const override { cat->performsConversion(); }
};

int main() {  // Client code
	DogFemale* dogFemale = new DogFemale;
	CatFemale* catFemale = new CatFemale;
	DogNature dogNature;
	//	dogNature.carryOutNature (catFemale);  // Will not compile of course since the parameter must be of type Dog*.
	ConversionAdapter* adaptedCat = new ConversionAdapter(catFemale);  // catFemale has adapted to become a Dog!

	dogNature.carryOutNature(dogFemale);
	dogNature.carryOutNature(adaptedCat);  // So now catFemale, in the form of adaptedCat, participates in the dogNature!
				// Note that catFemale is carrying out her own type of nature in dogNature though.

	delete adaptedCat;  // adaptedCat is not needed anymore
	delete catFemale; // catFemale is not needed anymore
	delete dogFemale; // dogFemale is not needed anymore, too
	return 0;
}

桥接模式用于将接口与其实现分离。这样做提供了灵活性,使得两者可以独立变化。

以下示例将输出

API1.circle at 1:2 7.5
API2.circle at 5:7 27.5
#include <iostream>

using namespace std;

/* Implementor*/
class DrawingAPI {
  public:
   virtual void drawCircle(double x, double y, double radius) = 0;
   virtual ~DrawingAPI() {}
};

/* Concrete ImplementorA*/
class DrawingAPI1 : public DrawingAPI {
  public:
   void drawCircle(double x, double y, double radius) {
      cout << "API1.circle at " << x << ':' << y << ' ' << radius << endl;
   }
};

/* Concrete ImplementorB*/
class DrawingAPI2 : public DrawingAPI {
public:
   void drawCircle(double x, double y, double radius) {
      cout << "API2.circle at " << x << ':' << y << ' ' <<  radius << endl;
   }
};

/* Abstraction*/
class Shape {
  public:
   virtual ~Shape() {}
   virtual void draw() = 0;
   virtual void resizeByPercentage(double pct) = 0;
};

/* Refined Abstraction*/
class CircleShape : public Shape {
  public:
   CircleShape(double x, double y,double radius, DrawingAPI *drawingAPI) :
	   m_x(x), m_y(y), m_radius(radius), m_drawingAPI(drawingAPI)
   {}
   void draw() {
      m_drawingAPI->drawCircle(m_x, m_y, m_radius);
   }
   void resizeByPercentage(double pct) {
      m_radius *= pct;
   }
  private:
   double m_x, m_y, m_radius;
   DrawingAPI *m_drawingAPI;
};

int main(void) {
   CircleShape circle1(1,2,3,new DrawingAPI1());
   CircleShape circle2(5,7,11,new DrawingAPI2());
   circle1.resizeByPercentage(2.5);
   circle2.resizeByPercentage(2.5);
   circle1.draw();
   circle2.draw();
   return 0;
}

组合模式使客户端可以一致地对待单个对象和对象的组合。组合模式可以表示这两种情况。在这个模式中,可以开发树状结构来表示部分-整体层次结构。

#include <vector>
#include <iostream> // std::cout
#include <memory> // std::auto_ptr
#include <algorithm> // std::for_each
using namespace std;
 
class Graphic
{
public:
  virtual void print() const = 0;
  virtual ~Graphic() {}
};
 
class Ellipse : public Graphic
{
public:
  void print() const {
    cout << "Ellipse \n";
  }
};
 
class CompositeGraphic : public Graphic
{
public:
  void print() const {
    for(Graphic * a: graphicList_) {
      a->print();
    }
  }
 
  void add(Graphic *aGraphic) {
    graphicList_.push_back(aGraphic);
  }
 
private:
  vector<Graphic*>  graphicList_;
};
 
int main()
{
  // Initialize four ellipses
  const auto_ptr<Ellipse> ellipse1(new Ellipse());
  const auto_ptr<Ellipse> ellipse2(new Ellipse());
  const auto_ptr<Ellipse> ellipse3(new Ellipse());
  const auto_ptr<Ellipse> ellipse4(new Ellipse());
 
  // Initialize three composite graphics
  const auto_ptr<CompositeGraphic> graphic(new CompositeGraphic());
  const auto_ptr<CompositeGraphic> graphic1(new CompositeGraphic());
  const auto_ptr<CompositeGraphic> graphic2(new CompositeGraphic());
 
  // Composes the graphics
  graphic1->add(ellipse1.get());
  graphic1->add(ellipse2.get());
  graphic1->add(ellipse3.get());
 
  graphic2->add(ellipse4.get());
 
  graphic->add(graphic1.get());
  graphic->add(graphic2.get());
 
  // Prints the complete graphic (four times the string "Ellipse")
  graphic->print();
  return 0;
}

装饰器

[编辑 | 编辑源代码]

装饰器模式有助于动态地向对象附加额外的行为或责任。装饰器为扩展功能提供了比子类化更灵活的替代方案。这也称为“包装器”。如果您的应用程序执行某种过滤,那么装饰器可能是值得考虑的模式。

#include <string>
#include <iostream>
using namespace std;

class Car //Our Abstract base class
{
        protected:
                string _str;
        public:
                Car()
                {
                        _str = "Unknown Car";
                }
 
                virtual string getDescription()
                {       
                        return _str;
                }
 
                virtual double getCost() = 0;
 
                virtual ~Car()
                {
                        cout << "~Car()\n";
                }
};
 
class OptionsDecorator : public Car //Decorator Base class
{
        public:
                virtual string getDescription() = 0;
 
                virtual ~OptionsDecorator()
                {
                        cout<<"~OptionsDecorator()\n";
                }
};
 
 
class CarModel1 : public Car
{       
        public:
                CarModel1()
                {
                        _str = "CarModel1";
                }
                virtual double getCost()
                {
                        return 31000.23;
                }
 
                ~CarModel1()
                {
                        cout<<"~CarModel1()\n";
                }
};
 
 
class Navigation: public OptionsDecorator
{
                Car *_b;
        public:
                Navigation(Car *b)
                {
                        _b = b;
                }
                string getDescription()
                {
                        return _b->getDescription() + ", Navigation";
                }
 
                double getCost()
                {
                        return 300.56 + _b->getCost();
                }
                ~Navigation()
                {
                        cout << "~Navigation()\n";
                        delete _b;
                }
};
 
class PremiumSoundSystem: public OptionsDecorator
{
                Car *_b;
        public:
                PremiumSoundSystem(Car *b)
                {
                        _b = b;
                }
                string getDescription()
                {
                        return _b->getDescription() + ", PremiumSoundSystem";
                }
 
                double getCost()
                {
                        return 0.30 + _b->getCost();
                }
                ~PremiumSoundSystem()
                {
                        cout << "~PremiumSoundSystem()\n";
                        delete _b;
                }
};
 
class ManualTransmission: public OptionsDecorator
{
                Car *_b;
        public:
                ManualTransmission(Car *b)
                {
                        _b = b;
                }
                string getDescription()
                {
		        return _b->getDescription()+ ", ManualTransmission";
                }
 
                double getCost()
                {
                        return 0.30 + _b->getCost();
                }
                ~ManualTransmission()
                {
                        cout << "~ManualTransmission()\n";
                        delete _b;
                }
};
 
int main()
{
        //Create our Car that we want to buy
        Car *b = new CarModel1();

        cout << "Base model of " << b->getDescription() << " costs $" << b->getCost() << "\n";  
            
        //Who wants base model let's add some more features
            
        b = new Navigation(b);
        cout << b->getDescription() << " will cost you $" << b->getCost() << "\n";
        b = new PremiumSoundSystem(b);
        b = new ManualTransmission(b);
        cout << b->getDescription() << " will cost you $" << b->getCost() << "\n";

        // WARNING! Here we leak the CarModel1, Navigation and PremiumSoundSystem objects!
        // Either we delete them explicitly or rewrite the Decorators to take 
        // ownership and delete their Cars when destroyed.
        delete b;

        return 0;
}

上面程序的输出是

Base model of CarModel1 costs $31000.2
CarModel1, Navigation will cost you $31300.8
CarModel1, Navigation, PremiumSoundSystem, ManualTransmission will cost you $31301.4
~ManualTransmission
~PremiumSoundSystem() 
~Navigation()
~CarModel1
~Car() 
~OptionsDecorator()
~Car() 
~OptionsDecorator()
~Car() 
~OptionsDecorator()
~Car()

另一个示例(C++14)

#include <iostream>
#include <string>
#include <memory>

class Interface {
	public:
		virtual ~Interface() { }
		virtual void write (std::string&) = 0;
};

class Core : public Interface {
	public:
	   	~Core() {std::cout << "Core destructor called.\n";}
	   	virtual void write (std::string& text) override {};  // Do nothing.
};

class Decorator : public Interface {
	private:
	   	std::unique_ptr<Interface> interface;
	public:
	   	Decorator (std::unique_ptr<Interface> c) {interface = std::move(c);}
	   	virtual void write (std::string& text) override {interface->write(text);}
};

class MessengerWithSalutation : public Decorator {
	private:
	   	std::string salutation;
	public:
	   	MessengerWithSalutation (std::unique_ptr<Interface> c, const std::string& str) : Decorator(std::move(c)), salutation(str) {}
	   	~MessengerWithSalutation() {std::cout << "Messenger destructor called.\n";}
	   	virtual void write (std::string& text) override {
		   	text = salutation + "\n\n" + text;
		 	Decorator::write(text);
		}
};

class MessengerWithValediction : public Decorator {
	private:
	   	std::string valediction;
	public:
	   	MessengerWithValediction (std::unique_ptr<Interface> c, const std::string& str) : Decorator(std::move(c)), valediction(str) {}
	   	~MessengerWithValediction() {std::cout << "MessengerWithValediction destructor called.\n";}
	   	virtual void write (std::string& text) override {
		 	Decorator::write(text);
			text += "\n\n" + valediction;
		}
};

int main() {
	const std::string salutation = "Greetings,";
	const std::string valediction = "Sincerly, Andy";
	std::string message1 = "This message is not decorated.";
	std::string message2 = "This message is decorated with a salutation.";
	std::string message3 = "This message is decorated with a valediction.";
	std::string message4 = "This message is decorated with a salutation and a valediction.";

	std::unique_ptr<Interface> messenger1 = std::make_unique<Core>();
	std::unique_ptr<Interface> messenger2 = std::make_unique<MessengerWithSalutation> (std::make_unique<Core>(), salutation);
	std::unique_ptr<Interface> messenger3 = std::make_unique<MessengerWithValediction> (std::make_unique<Core>(), valediction);
	std::unique_ptr<Interface> messenger4 = std::make_unique<MessengerWithValediction> (std::make_unique<MessengerWithSalutation>
  						                  (std::make_unique<Core>(), salutation), valediction);
	
	messenger1->write(message1);
	std::cout << message1 << '\n';
	std::cout << "\n------------------------------\n\n";

	messenger2->write(message2);
	std::cout << message2 << '\n';
	std::cout << "\n------------------------------\n\n";

	messenger3->write(message3);
	std::cout << message3 << '\n';
	std::cout << "\n------------------------------\n\n";

	messenger4->write(message4);
	std::cout << message4 << '\n';
	std::cout << "\n------------------------------\n\n";
}

上面程序的输出是

This message is not decorated.

------------------------------

Greetings,

This message is decorated with a salutation.

------------------------------

This message is decorated with a valediction.

Sincerly, Andy

------------------------------

Greetings,

This message is decorated with a salutation and a valediction.

Sincerly, Andy

------------------------------

MessengerWithValediction destructor called.
Messenger destructor called.
Core destructor called.
MessengerWithValediction destructor called.
Core destructor called.
Messenger destructor called.
Core destructor called.
Core destructor called.

外观模式通过向客户端提供一个接口来隐藏系统的复杂性,从该接口,客户端可以以统一的接口访问系统。外观定义了一个更高层的接口,使子系统更容易使用。例如,让一个类方法通过调用其他几个类来执行一个复杂的过程。

/*Facade is one of the easiest patterns I think... And this is very simple example.

Imagine you set up a smart house where everything is on remote. So to turn the lights on you push lights on button - And same for TV,
AC, Alarm, Music, etc...

When you leave a house you would need to push a 100 buttons to make sure everything is off and are good to go which could be little 
annoying if you are lazy like me 
so I defined a Facade for leaving and coming back. (Facade functions represent buttons...) So when I come and leave I just make one 
call and it takes care of everything...
*/

#include <string>
#include <iostream>

using namespace std;

class Alarm
{
public:
	void alarmOn()
	{
		cout << "Alarm is on and house is secured"<<endl;
	}

	void alarmOff()
	{
		cout << "Alarm is off and you can go into the house"<<endl;
	}
};

class Ac
{
public:
	void acOn()
	{
		cout << "Ac is on"<<endl;
	}

	void acOff()
	{
		cout << "AC is off"<<endl;
	}
};

class Tv
{
public:
	void tvOn()
	{
		cout << "Tv is on"<<endl;
	}

	void tvOff()
	{
		cout << "TV is off"<<endl;
	}
};

class HouseFacade
{
	Alarm alarm;
	Ac ac;
	Tv tv;

public:
	HouseFacade(){}

	void goToWork()
	{
		ac.acOff();
		tv.tvOff();
		alarm.alarmOn();
	}

	void comeHome()
	{
		alarm.alarmOff();
		ac.acOn();
		tv.tvOn();
	}
};

int main()
{
	HouseFacade hf;

	//Rather than calling 100 different on and off functions thanks to facade I only have 2 functions...
	hf.goToWork();
	hf.comeHome();
}

上面程序的输出是

AC is off
TV is off
Alarm is on and house is secured
Alarm is off and you can go into the house
Ac is on
Tv is on

用于通过共享对象属性来节省内存(基本上)的模式。想象一下大量的相似对象,它们的大部分属性都相同。自然地,可以将这些属性从这些对象中移到一些外部数据结构中,并为每个对象提供指向该数据结构的链接。

#include <iostream>
#include <string>
#include <vector>

#define NUMBER_OF_SAME_TYPE_CHARS 3;

/* Actual flyweight objects class (declaration) */
class FlyweightCharacter;

/*
	FlyweightCharacterAbstractBuilder is a class holding the properties which are shared by
	many objects. So instead of keeping these properties in those objects we keep them externally, making
	objects flyweight. See more details in the comments of main function.
*/
class FlyweightCharacterAbstractBuilder {
	FlyweightCharacterAbstractBuilder() {}
	~FlyweightCharacterAbstractBuilder() {}
public:
	static std::vector<float> fontSizes; // lets imagine that sizes may be of floating point type
	static std::vector<std::string> fontNames; // font name may be of variable length (lets take 6 bytes is average)

	static void setFontsAndNames();
	static FlyweightCharacter createFlyweightCharacter(unsigned short fontSizeIndex,
		unsigned short fontNameIndex,
		unsigned short positionInStream);
};

std::vector<float> FlyweightCharacterAbstractBuilder::fontSizes(3);
std::vector<std::string> FlyweightCharacterAbstractBuilder::fontNames(3);
void FlyweightCharacterAbstractBuilder::setFontsAndNames() {
	fontSizes[0] = 1.0;
	fontSizes[1] = 1.5;
	fontSizes[2] = 2.0;

	fontNames[0] = "first_font";
	fontNames[1] = "second_font";
	fontNames[2] = "third_font";
}

class FlyweightCharacter {
	unsigned short fontSizeIndex; // index instead of actual font size
	unsigned short fontNameIndex; // index instead of font name

	unsigned positionInStream;

public:

	FlyweightCharacter(unsigned short fontSizeIndex, unsigned short fontNameIndex, unsigned short positionInStream):
		fontSizeIndex(fontSizeIndex), fontNameIndex(fontNameIndex), positionInStream(positionInStream) {}
	void print() {
		std::cout << "Font Size: " << FlyweightCharacterAbstractBuilder::fontSizes[fontSizeIndex]
			<< ", font Name: " << FlyweightCharacterAbstractBuilder::fontNames[fontNameIndex]
			<< ", character stream position: " << positionInStream << std::endl;
	}
	~FlyweightCharacter() {}
};

FlyweightCharacter FlyweightCharacterAbstractBuilder::createFlyweightCharacter(unsigned short fontSizeIndex, unsigned short fontNameIndex, unsigned short positionInStream) {
	FlyweightCharacter fc(fontSizeIndex, fontNameIndex, positionInStream);

	return fc;
}

int main(int argc, char** argv) {
	std::vector<FlyweightCharacter> chars;

	FlyweightCharacterAbstractBuilder::setFontsAndNames();
	unsigned short limit = NUMBER_OF_SAME_TYPE_CHARS;

	for (unsigned short i = 0; i < limit; i++) {
		chars.push_back(FlyweightCharacterAbstractBuilder::createFlyweightCharacter(0, 0, i));
		chars.push_back(FlyweightCharacterAbstractBuilder::createFlyweightCharacter(1, 1, i + 1 * limit));
		chars.push_back(FlyweightCharacterAbstractBuilder::createFlyweightCharacter(2, 2, i + 2 * limit));
	}
	/*
		Each char stores links to its fontName and fontSize so what we get is:

		each object instead of allocating 6 bytes (convention above) for string
		and 4 bytes for float allocates 2 bytes for fontNameIndex and fontSizeIndex.

		That means for each char we save 6 + 4 - 2 - 2 = 6 bytes.
		Now imagine we have NUMBER_OF_SAME_TYPE_CHARS = 1000 i.e. with our code
		we will have 3 groups of chars with 1000 chars in each group which will save 
		3 * 1000 * 6 - (3 * 6 + 3 * 4) = 17970 saved bytes.

		3 * 6 + 3 * 4 is a number of bytes allocated by FlyweightCharacterAbstractBuilder.

		So the idea of the pattern is to move properties shared by many objects to some
		external container. The objects in that case don't store the data themselves they
		store only links to the data which saves memory and make the objects lighter.
		The data size of properties stored externally may be significant which will save REALLY
		huge amount of memory and will make each object super light in comparison to its counterpart.
		That's where the name of the pattern comes from: flyweight (i.e. very light).
	*/
	for (unsigned short i = 0; i < chars.size(); i++) {
		chars[i].print();
	}

	std::cin.get(); return 0;
}

代理模式将为另一个对象提供一个替代品或占位符,以控制对其的访问。它用于在需要用更简单的对象来表示复杂对象时使用。如果对象的创建很昂贵,则可以将其推迟到真正需要它的时候,同时可以用一个更简单的对象作为占位符。这个占位符对象被称为复杂对象的“代理”。

#include <iostream>
#include <memory>

class ICar {
 public:
  virtual ~ICar() { std::cout << "ICar destructor!" << std::endl; }

  virtual void DriveCar() = 0;
};

class Car : public ICar {
 public:
  void DriveCar() override { std::cout << "Car has been driven!" << std::endl; }
};

class ProxyCar : public ICar {
 public:
  ProxyCar(int driver_age) : driver_age_(driver_age) {}

  void DriveCar() override {
    if (driver_age_ > 16) {
      real_car_->DriveCar();
    } else {
      std::cout << "Sorry, the driver is too young to drive." << std::endl;
    }
  }

 private:
  std::unique_ptr<ICar> real_car_ = std::make_unique<Car>();
  int driver_age_;
};

int main() {
  std::unique_ptr<ICar> car = std::make_unique<ProxyCar>(16);
  car->DriveCar();

  car = std::make_unique<ProxyCar>(25);
  car->DriveCar();
  return 0;
}

奇特的递归模板

[编辑 | 编辑源代码]

这种技术更广为人知的是Mixin。Mixin 在文献中被描述为表达抽象的强大工具[需要引用]

基于接口的编程 (IBP)

[编辑 | 编辑源代码]

基于接口的编程与模块化编程和面向对象编程密切相关,它将应用程序定义为相互耦合的模块(相互连接,并通过接口相互插入)的集合。模块可以分离、替换或升级,而无需影响其他模块的内容。

整个系统的复杂性大大降低。基于接口的编程在模块化编程的基础上更进一步,它要求向这些模块添加接口。因此,整个系统被视为组件以及帮助它们协同工作的接口。

基于接口的编程提高了应用程序的模块化,从而提高了其在后续开发周期中的可维护性,特别是在每个模块必须由不同的团队开发的情况下。它是一种众所周知的已有很长时间的方法,并且是诸如 CORBA 之类的框架背后的核心技术。 [需要引用]

这在第三方为已建立的系统开发附加组件时尤其方便。他们只需要开发满足父应用程序供应商指定的接口的组件即可。

因此,接口的发布者保证他不会更改接口,而订阅者同意完全实现接口,没有任何偏差。因此,接口被称为契约协议,基于此的编程范式被称为“基于接口的编程”。

华夏公益教科书