Decorator Pattern Method. How to avoid these repetitions?

How to avoid the repeated lines indicated below?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <iostream>

class Player {
	private:
		std::string name;
		int hitPoints;
	public:
		Player() = default;
		Player (const std::string& newName, int hp): name (newName), hitPoints (hp) {}
		virtual ~Player() = default;
		virtual std::string Name() const {return name;}
		virtual int HitPoints() const {return hitPoints;}
		virtual void action() = 0;
		virtual void losesHitPoints (int num) {hitPoints -= num;  display();  if (hitPoints <= 0) dies();}
		void display() const {std::cout << name << "'s hit points = " << hitPoints << ".\n";}
	private:
		void dies() {std::cout << Name() << " dies!" << std::endl;  delete this;}
};

struct Fighter: public Player {
	Fighter() = default;
	Fighter (const std::string& name, int hp): Player (name, hp) {}
	virtual void action() override {std::cout << Name() << "'s action is normal.\n";}
};

struct MagicUser: public Player {
	int numSpells;
	MagicUser() = default;
	MagicUser (const std::string& name, int hp, int spells): Player (name, hp), numSpells (spells) {}
	int NumSpells() const {return numSpells;}
	virtual void action() override {std::cout << Name() << "'s action is normal.\n";}
};

template <class PLAYER_TYPE>
struct PlayerDecorator: public Player {
	virtual void action() = 0;
	virtual ~PlayerDecorator() = default;
};

template <class PLAYER_TYPE>
struct PlayerUnderHaste: public PlayerDecorator<PLAYER_TYPE>, public PLAYER_TYPE {
	PLAYER_TYPE* playerType;
	PlayerUnderHaste (PLAYER_TYPE* player): playerType (player) {}
	virtual void action() override {std::cout << Name() << " under Haste Spell.  Two attacks per round!\n";}
                // Any generic way to inherit these 3 (unchanged) lines from Player?
	virtual std::string Name() const override {return playerType->Name();}
	virtual int HitPoints() const override {return playerType->HitPoints();}
	virtual void losesHitPoints (int num) override {playerType->losesHitPoints (num);}
};  // Do I have to specialize PlayerUnderHaste<MagicUser> just for MagicUser::NumSpells() ?

template <class PLAYER_TYPE>
struct PlayerInvisible: public PlayerDecorator<PLAYER_TYPE>, public PLAYER_TYPE {
	PLAYER_TYPE* playerType;
	PlayerInvisible (PLAYER_TYPE* player): playerType (player) {}
	virtual void action() override {std::cout << Name () << " under Invisibility Spell.  Cannot be hit!\n";}
                // How to avoid rewriting these 3 lines again?
	virtual std::string Name() const override {return playerType->Name();}
	virtual int HitPoints() const override {return playerType->HitPoints();}
	virtual void losesHitPoints (int num) override {playerType->losesHitPoints (num);}
};  // Do I have to specialize PlayerInvisible<MagicUser> just for MagicUser::NumSpells() ?

int main() {
	Fighter* fighter = new Fighter ("Rex the Fighter", 8);
	MagicUser* magicUser = new MagicUser ("Merlin the Magic User", 4);
	fighter->display();  magicUser->display();
	fighter->action();  magicUser->action();
	std::cout << "Haste spell cast on Fighter and Magic User!" << std::endl;
	fighter = new PlayerUnderHaste<Fighter> (fighter);
	magicUser = new PlayerUnderHaste<MagicUser> (magicUser);
	fighter->action();  magicUser->action();
	std::cout << fighter->Name() << " hit by monster!  Loses 6 hit points!" << std::endl;
	fighter->losesHitPoints (6);
	std::cout << "Invisibility spell cast on Fighter and Magic User!" << std::endl;
	fighter = new PlayerInvisible<Fighter> (fighter);
	magicUser = new PlayerInvisible<MagicUser> (magicUser);
	fighter->action();  magicUser->action();
	std::cout << "Though invisible, " << magicUser->Name() << " is caught in a fireball blast!  10 hit points lost!" << std::endl;
	magicUser->losesHitPoints (10);
	std::cin.get();
}

The need to avoid the repetitions because there will be many such virtual function overrides and many other Decorator classes. The output is correct at least. In case you want to see:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Rex the Fighter's hit points = 8.
Merlin the Magic User's hit points = 4.
Rex the Fighter's action is normal.
Merlin the Magic User's action is normal.
Haste spell cast on Fighter and Magic User!
Rex the Fighter under Haste Spell.  Two attacks per round!
Merlin the Magic User under Haste Spell.  Two attacks per round!
Rex the Fighter hit by monster!  Loses 6 hit points!
Rex the Fighter's hit points = 2.
Invisibility spell cast on Fighter and Magic User!
Rex the Fighter under Invisibility Spell.  Cannot be hit!
Merlin the Magic User under Invisibility Spell.  Cannot be hit!
Though invisible, Merlin the Magic User is caught in a fireball blast!  10 hit points lost!
Merlin the Magic User's hit points = -6.
Merlin the Magic User dies!
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <iostream>

class Player {
    private:
		std::string name;
		int hitPoints;
	public:

		Player() = default;
		Player (const std::string& newName, int hp): name (newName), hitPoints (hp) {}
		virtual ~Player() = default;
		virtual std::string Name() const {return name;}
		virtual int HitPoints() const {return hitPoints;}
		virtual void action() = 0;
		virtual void losesHitPoints (int num) {hitPoints -= num;  display();  if (hitPoints <= 0) dies();}
		void display() const {std::cout << name << "'s hit points = " << hitPoints << ".\n";}
	private:
		void dies() {std::cout << Name() << " dies!" << std::endl;  delete this;}
};

struct Fighter: public Player {
	Fighter() = default;
	Fighter (const std::string& name, int hp): Player (name, hp) {}
	virtual void action() override {std::cout << Name() << "'s action is normal.\n";}
};

struct MagicUser: public Player {
	MagicUser() = default;
	MagicUser (const std::string& name, int hp): Player (name, hp) {}
	virtual void action() override {std::cout << Name() << "'s action is normal.\n";}
};

struct PlayerDecorator: public Player {
	virtual void action() = 0;

	Player* impl ; // owning pointer ( use std::unique_ptr )
	explicit PlayerDecorator( Player* p ) : impl(p) {}
        ~PlayerDecorator() { delete impl ; } // *** added ***

	virtual std::string Name() const override {return impl->Name();}
	virtual int HitPoints() const override {return impl->HitPoints();}
	virtual void losesHitPoints (int num) override {impl->losesHitPoints (num);}
};

struct PlayerUnderHaste: public PlayerDecorator  {

        using PlayerDecorator::PlayerDecorator ;

	virtual void action() override
	{std::cout << Name() << " under Haste Spell.  Two attacks per round!\n";}
};

struct PlayerInvisible: public PlayerDecorator {

        using PlayerDecorator::PlayerDecorator ;

	virtual void action() override
	{std::cout << Name () << " under Invisibility Spell.  Cannot be hit!\n";}
};

int main() {
	Player* fighter = new Fighter( "Rex the Fighter", 8);
	Player* magicUser = new MagicUser("Merlin the Magic User", 4);
	fighter->display();  magicUser->display();
	fighter->action();  magicUser->action();

	std::cout << "Haste spell cast on Fighter and Magic User!" << std::endl;
	fighter =  new PlayerUnderHaste(fighter) ;
	magicUser = new PlayerUnderHaste(magicUser) ;
	fighter->action();  magicUser->action();
	std::cout << fighter->Name() << " hit by monster!  Loses 6 hit points!" << std::endl;
	fighter->losesHitPoints (6);

	std::cout << "Invisibility spell cast on Fighter and Magic User!" << std::endl;
	fighter = new PlayerInvisible(fighter);
	magicUser = new PlayerInvisible(magicUser);
	fighter->action();  magicUser->action();

	std::cout << "Though invisible, " << magicUser->Name() << " is caught in a fireball blast!  10 hit points lost!" << std::endl;
	magicUser->losesHitPoints (10);  // magicUser deleted automatically, but any leaking??? 
}

http://coliru.stacked-crooked.com/a/9a1145259922a046
Thanks. JLBorges, I'm glad that the solution to the repetition problem is only one line (thank goodness to c++11 features!). But I editted my question while you were posting your solution I think. MagicUser has a data member numSpells that Player does not have. Now magicUser->NumSpells() (and castsSpell()) is lost because PlayerDecorator has lost its template parameter. If I go back with that template, do I have to specialize PlayerUnderHaste<MagicUser> and such just for MagicUser::NumSpells() and MagicUser::castsSpell() ?
Last edited on
Okay, here is my solution using template specialization for MagicUser, and also variadic templates to cover the general case of a player being under multiple (any number of) spells.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <iostream>
#include <list>

enum Spell {SleepSpell, MagicMissile, CharmPerson, Light, Invisibility, Haste};

class Player {
    private:
		std::string name;
		int hitPoints;
		std::list<Spell> spellsCastOnPlayer;
	public:
		Player() = default;
		Player (const std::string& newName, int hp): name (newName), hitPoints (hp) {}
		virtual ~Player() = default;
		virtual std::string Name() const {return name;}
		virtual int HitPoints() const {return hitPoints;}
		virtual const std::list<Spell>& SpellsCastOnPlayer() const {return spellsCastOnPlayer;}
		virtual void SpellsCastOnPlayerEmplaceBack (Spell spell) {spellsCastOnPlayer.emplace_back (spell);}
		virtual void action() = 0;
		virtual void losesHitPoints (int num) {hitPoints -= num;  display();  if (hitPoints <= 0) dies();}
		void display() const {std::cout << name << "'s hit points = " << hitPoints << ".\n";}
	private:
		void dies() {std::cout << Name() << " dies!" << std::endl;  delete this;}
};

struct Fighter: virtual public Player {
	public:
		Fighter() = default;
		Fighter (const std::string& name, int hp): Player (name, hp) {}
		virtual void action() override {std::cout << Name() << "'s action is normal.\n";}
};

class MagicUser: virtual public Player {
	private:
		int numSpells;
	public:
		MagicUser() = default;
		MagicUser (const std::string& name, int hp, int spells): Player (name, hp), numSpells (spells) {}
		virtual int NumSpells() const {return numSpells;}
		virtual void castsSpell() {numSpells--;  std::cout << Name() << " casts a spell, now has " << numSpells << " spells left.\n";}
		virtual void action() override {std::cout << Name() << "'s action is normal.\n";}
};

template <class PlayerType, Spell...spells>
struct PlayerDecorator: virtual public PlayerType {
	Player* playerType; // owning pointer ( use std::unique_ptr )
	PlayerDecorator() = default;
    explicit PlayerDecorator (Player* p): playerType(p) {}
	~PlayerDecorator() {delete playerType;}
	virtual void action() = 0;
	virtual std::string Name() const override {return playerType->Name();}
	virtual int HitPoints() const override {return playerType->HitPoints();}
	virtual void losesHitPoints (int num) override {playerType->losesHitPoints (num);}
	virtual const std::list<Spell>& SpellsCastOnPlayer() const override {return playerType->SpellsCastOnPlayer();}
	virtual void SpellsCastOnPlayerEmplaceBack (Spell spell) override {playerType->SpellsCastOnPlayerEmplaceBack (spell);}
};

template <Spell...spells>
struct PlayerDecorator<MagicUser, spells...>: virtual public MagicUser {
	MagicUser* magicUser;
	PlayerDecorator() = default;
    explicit PlayerDecorator (MagicUser* m): magicUser (m) {}
	~PlayerDecorator() {delete magicUser;}
	virtual void action() = 0;
	virtual std::string Name() const override {return magicUser->Name();}
	virtual int HitPoints() const override {return magicUser->HitPoints();}
	virtual void losesHitPoints (int num) override {magicUser->losesHitPoints (num);}
	virtual const std::list<Spell>& SpellsCastOnPlayer() const override {return magicUser->SpellsCastOnPlayer();}
	virtual void SpellsCastOnPlayerEmplaceBack (Spell spell) override {magicUser->SpellsCastOnPlayerEmplaceBack (spell);}
	virtual int NumSpells() const override {return magicUser->NumSpells();}
	virtual void castsSpell() override {magicUser->castsSpell();}
};

template <class PlayerType, Spell...spells>
struct PlayerUnderSpell: virtual public PlayerDecorator<PlayerType, spells...>, virtual public PlayerType {
	using PlayerDecorator<PlayerType, spells...>::PlayerDecorator;
	virtual void action() override {
		std::cout << PlayerDecorator<PlayerType, spells...>::Name() << " is now under the following spells:\n";
		for (Spell x: {spells...})
			PlayerDecorator<PlayerType, spells...>::SpellsCastOnPlayerEmplaceBack (x);
		for (Spell x: PlayerDecorator<PlayerType, spells...>::SpellsCastOnPlayer())
			std::cout << "Spell #" << x << "  ";
		std::cout << std::endl;
	}
};

int main() {
	Fighter* fighter = new Fighter ("Rex the Fighter", 8);
	MagicUser* magicUser = new MagicUser ("Merlin the Magic User", 4, 5);
	fighter->display();  magicUser->display();
	fighter->action();  magicUser->action();
	magicUser->castsSpell();
	
	std::cout << "Haste spell cast on Fighter and Magic User!" << std::endl;
	fighter =  new PlayerUnderSpell<Fighter, Haste> (fighter);
	magicUser = new PlayerUnderSpell<MagicUser, Haste> (magicUser);
	fighter->action();  magicUser->action();
	magicUser->castsSpell();
	std::cout << fighter->Name() << " hit by monster!  Loses 6 hit points!" << std::endl;
	fighter->losesHitPoints (6);

	std::cout << "Invisibility spell cast on Fighter and Magic User!" << std::endl;
	fighter = new PlayerUnderSpell<Fighter, Invisibility> (fighter);
	magicUser = new PlayerUnderSpell<MagicUser, Invisibility> (magicUser);
	fighter->action();  magicUser->action();
	magicUser->castsSpell();

	std::cout << "Though invisible, " << magicUser->Name() << " is caught in a fireball blast!  10 hit points lost!" << std::endl;
	magicUser->losesHitPoints (10);  // magicUser deleted automatically, and no leaking because of PlayerDecorator's destructor
	std::cin.get();
} 

Output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Rex the Fighter's hit points = 8.
Merlin the Magic User's hit points = 4.
Rex the Fighter's action is normal.
Merlin the Magic User's action is normal.
Merlin the Magic User casts a spell, now has 4 spells left.
Haste spell cast on Fighter and Magic User!
Rex the Fighter is now under the following spells:
Spell #10
Merlin the Magic User is now under the following spells:
Spell #10
Merlin the Magic User casts a spell, now has 3 spells left.
Rex the Fighter hit by monster!  Loses 6 hit points!
Rex the Fighter's hit points = 2.
Invisibility spell cast on Fighter and Magic User!
Rex the Fighter is now under the following spells:
Spell #10  Spell #5
Merlin the Magic User is now under the following spells:
Spell #10  Spell #5
Merlin the Magic User casts a spell, now has 2 spells left.
Though invisible, Merlin the Magic User is caught in a fireball blast!  10 hit p
oints lost!
Merlin the Magic User's hit points = -6.
Merlin the Magic User dies!

Last edited on
Topic archived. No new replies allowed.