patterncppMinor
Use of Mediator Pattern or Proxy Pattern for a game
Viewed 0 times
proxygameformediatorusepattern
Problem
When a
In my code below, I'm using a mediator, but since such a specialized mediator (used only for this spell) do not pertain to all living beings (most living beings cannot even cast spells), I've placed the mediator data member only within a specific State class that is only created when the spell is cast. Here I have the mediator registering the summoned monsters when the spell is cast, dispatching the commands from the wizard to the summoned monsters, unregistering a summoned monster when it dies, and destroying all the summoned monsters in the event that the wizard himself dies (or loses his concentration).
Note: all classes are structs here for simplicity.
```
#include
#include
#include
struct Mediator {
struct LivingBeing* being;
Mediator (LivingBeing* b) : being(b) {}
};
struct MonsterSummoningMediator : Mediator {
std::list summonedMonsters;
using Mediator::Mediator;
~MonsterSummoningMediator() {destroySummonedMonsters();}
void registerSummonedMonster (SummonedMonster* m) {summonedMonsters.emplace_back(m);}
void unregisterSummonedMonster (SummonedMonster* m) {summonedMonsters.remove(m);}
inline void distributeAttackCommand();
inline void distributeShieldMeCommand();
inline void destroySummonedMonsters();
};
struct State {
LivingBeing* being;
State (LivingBeing* b) : being(b) {}
virtual ~State() = default;
};
struct MonsterSummonerState : State {
MonsterSummoningMediator* mediator;
MonsterSummonerState (LivingBeing* being) :
State(being), mediator(new MonsterSummoningMediator(being)) {}
~MonsterSummonerState() {delete mediator;}
void addSummonedMonster(SummonedMonster* m) {mediator->registerSummonedMonster(m);}
};
struct LivingBei
Wizard casts the Monster Summoning spell, all of the summoned monsters shall follow his command. Should the Wizard interact with his summoned monsters directly (what are the potential pitfalls in this case?) or indirectly through a mediator?In my code below, I'm using a mediator, but since such a specialized mediator (used only for this spell) do not pertain to all living beings (most living beings cannot even cast spells), I've placed the mediator data member only within a specific State class that is only created when the spell is cast. Here I have the mediator registering the summoned monsters when the spell is cast, dispatching the commands from the wizard to the summoned monsters, unregistering a summoned monster when it dies, and destroying all the summoned monsters in the event that the wizard himself dies (or loses his concentration).
Note: all classes are structs here for simplicity.
```
#include
#include
#include
struct Mediator {
struct LivingBeing* being;
Mediator (LivingBeing* b) : being(b) {}
};
struct MonsterSummoningMediator : Mediator {
std::list summonedMonsters;
using Mediator::Mediator;
~MonsterSummoningMediator() {destroySummonedMonsters();}
void registerSummonedMonster (SummonedMonster* m) {summonedMonsters.emplace_back(m);}
void unregisterSummonedMonster (SummonedMonster* m) {summonedMonsters.remove(m);}
inline void distributeAttackCommand();
inline void distributeShieldMeCommand();
inline void destroySummonedMonsters();
};
struct State {
LivingBeing* being;
State (LivingBeing* b) : being(b) {}
virtual ~State() = default;
};
struct MonsterSummonerState : State {
MonsterSummoningMediator* mediator;
MonsterSummonerState (LivingBeing* being) :
State(being), mediator(new MonsterSummoningMediator(being)) {}
~MonsterSummonerState() {delete mediator;}
void addSummonedMonster(SummonedMonster* m) {mediator->registerSummonedMonster(m);}
};
struct LivingBei
Solution
The flaws of using the proxy pattern demonstrated above will be removed if I use the adapter pattern instead:
The resulting output does not show the problems in the question's output:
I still have to investigate if this works in more complex constructions though.
Update: I've experimented this new design with more complex constructions and its seems to be working fine. Here is my latest design, in case you wanted to see the whole thing:
`#include
#include
#include
struct LivingBeing;
std::list allBeingsPresent;
struct Mediator {
LivingBeing* being;
Mediator (LivingBeing* b) : being(b) {}
};
struct MonsterSummoningMediator : Mediator {
std::list summonedMonsters;
using Mediator::Mediator;
~MonsterSummoningMediator() {std::cout name losesHitPoints(20); target->dies();}
};
struct Monster : LivingBeing {
using LivingBeing::LivingBeing;
virtual ~Monster() {std::cout spellsKnown;
std::string specialty;
int numWands;
template
GoblinWizard (const LivingBeing::Data& data, const std::list& spells, const std::string s, int num, Args&&... args) :
Goblin(data, std::forward(args)...), spellsKnown(spells), specialty(s), numWands(num) {}
};
struct Lich : Monster {
struct Data {
std::list spellsKnown;
int undeadLevel;
std::string deity;
};
Data lichData;
template
Lich (const LivingBeing::Data& data, const Data& lData, Args&&... args) : Monster(data, std::forward(args)...), lichData(lData) {}
};
struct SummonedMonster : Monster {
LivingBeing* summoner;
SummonedMonster (LivingBeing* s, const LivingBeing::Data& data) : Monster(data, SummonedTag{}), summoner(s) {}
void receiveAttackCommand() {std::cout name name
struct Summoned : SummonedMonster, T { // Adapter Pattern
template
Summoned (LivingBeing* summoner, const LivingBeing::Data& data, Args&&... args) : SummonedMonster(summoner, data), T(data, std::forward(args)..., SummonedTag{}) {
allBeingsPresent.emplace_back(dynamic_cast(this)); // This is the pointer that we want to insert into allBeingsPresent.
}
};
struct CharacterClass : LivingBeing {
using LivingBeing::LivingBeing;
};
struct Fighter : CharacterClass {
using CharacterClass::CharacterClass;
};
struct Wizard : CharacterClass {
using CharacterClass::CharacterClass;
void castsMonsterSummoning() {
state = new MonsterSummonerState(this);
MonsterSummoningMediator* monsterSummoningMediator = getMonsterSummon
struct LivingBeing {
struct SummonedTag {};
std::string name;
State* state;
LivingBeing() = default;
LivingBeing (const std::string& n) : name(n) {allBeingsPresent.emplace_back(this);}
LivingBeing (const std::string& n, SummonedTag) : name(n) {} // Do NOT call 'allBeingsPresent.emplace_back(this);' else there will be duplication.
// ...
};
struct Monster : LivingBeing {
using LivingBeing::LivingBeing;
// ...
};
struct Goblin : Monster { using Monster::Monster; };
struct SummonedMonster : Monster {
LivingBeing* summoner;
SummonedMonster (LivingBeing* s, const std::string& name) : Monster(name), summoner(s) {}
// ...
}
template
struct Summoned : SummonedMonster, T { // *** Adapter Pattern
Summoned (LivingBeing* summoner, const std::string& name) : SummonedMonster(summoner, name),
T(name, SummonedTag{}) {}
};
template
void Wizard::castsMonsterSummoning() {
state = new MonsterSummonerState(this);
MonsterSummoningMediator* monsterSummoningMediator = getMonsterSummoningMediator();
for (int i = 0; i registerSummonedMonster(new Summoned(this, "Summoned Monster"));
}
}The resulting output does not show the problems in the question's output:
Wizard::castsMonsterSummoning() called by Merlin.
monsterSummoningMediator->summonedMonsters.size() = 5
Merlin is present.
Borg is present.
Summoned Monster is present.
Summoned Monster is present.
Summoned Monster is present.
Summoned Monster is present.
Summoned Monster is present.
Summoned Monster will follow Merlin's order to attack.
Summoned Monster will follow Merlin's order to attack.
Summoned Monster will follow Merlin's order to attack.
Summoned Monster will follow Merlin's order to attack.
Summoned Monster will follow Merlin's order to attack.
Summoned Monster will follow Merlin's order to shield him.
Summoned Monster will follow Merlin's order to shield him.
Summoned Monster will follow Merlin's order to shield him.
Summoned Monster will follow Merlin's order to shield him.
Summoned Monster will follow Merlin's order to shield him.
Borg attacks Summoned Monster.
Summoned Monster dies.
Merlin dies.
Merlin's Monster Summoning spell has ended.
MonsterSummoningMediator destroyed.
Summoned Monster destroyed.
Summoned Monster destroyed.
Summoned Monster destroyed.
Summoned Monster destroyed.
Summoned Monster destroyed. // Hmmm... duplicate destructor call (seems harmless though).
Summoned Monster destroyed.
Summoned Monster destroyed.
Summoned Monster destroyed.
I still have to investigate if this works in more complex constructions though.
Update: I've experimented this new design with more complex constructions and its seems to be working fine. Here is my latest design, in case you wanted to see the whole thing:
`#include
#include
#include
struct LivingBeing;
std::list allBeingsPresent;
struct Mediator {
LivingBeing* being;
Mediator (LivingBeing* b) : being(b) {}
};
struct MonsterSummoningMediator : Mediator {
std::list summonedMonsters;
using Mediator::Mediator;
~MonsterSummoningMediator() {std::cout name losesHitPoints(20); target->dies();}
};
struct Monster : LivingBeing {
using LivingBeing::LivingBeing;
virtual ~Monster() {std::cout spellsKnown;
std::string specialty;
int numWands;
template
GoblinWizard (const LivingBeing::Data& data, const std::list& spells, const std::string s, int num, Args&&... args) :
Goblin(data, std::forward(args)...), spellsKnown(spells), specialty(s), numWands(num) {}
};
struct Lich : Monster {
struct Data {
std::list spellsKnown;
int undeadLevel;
std::string deity;
};
Data lichData;
template
Lich (const LivingBeing::Data& data, const Data& lData, Args&&... args) : Monster(data, std::forward(args)...), lichData(lData) {}
};
struct SummonedMonster : Monster {
LivingBeing* summoner;
SummonedMonster (LivingBeing* s, const LivingBeing::Data& data) : Monster(data, SummonedTag{}), summoner(s) {}
void receiveAttackCommand() {std::cout name name
struct Summoned : SummonedMonster, T { // Adapter Pattern
template
Summoned (LivingBeing* summoner, const LivingBeing::Data& data, Args&&... args) : SummonedMonster(summoner, data), T(data, std::forward(args)..., SummonedTag{}) {
allBeingsPresent.emplace_back(dynamic_cast(this)); // This is the pointer that we want to insert into allBeingsPresent.
}
};
struct CharacterClass : LivingBeing {
using LivingBeing::LivingBeing;
};
struct Fighter : CharacterClass {
using CharacterClass::CharacterClass;
};
struct Wizard : CharacterClass {
using CharacterClass::CharacterClass;
void castsMonsterSummoning() {
state = new MonsterSummonerState(this);
MonsterSummoningMediator* monsterSummoningMediator = getMonsterSummon
Code Snippets
struct LivingBeing {
struct SummonedTag {};
std::string name;
State* state;
LivingBeing() = default;
LivingBeing (const std::string& n) : name(n) {allBeingsPresent.emplace_back(this);}
LivingBeing (const std::string& n, SummonedTag) : name(n) {} // Do NOT call 'allBeingsPresent.emplace_back(this);' else there will be duplication.
// ...
};
struct Monster : LivingBeing {
using LivingBeing::LivingBeing;
// ...
};
struct Goblin : Monster { using Monster::Monster; };
struct SummonedMonster : Monster {
LivingBeing* summoner;
SummonedMonster (LivingBeing* s, const std::string& name) : Monster(name), summoner(s) {}
// ...
}
template <typename T>
struct Summoned : SummonedMonster, T { // *** Adapter Pattern
Summoned (LivingBeing* summoner, const std::string& name) : SummonedMonster(summoner, name),
T(name, SummonedTag{}) {}
};
template <typename T>
void Wizard::castsMonsterSummoning() {
state = new MonsterSummonerState(this);
MonsterSummoningMediator* monsterSummoningMediator = getMonsterSummoningMediator();
for (int i = 0; i < 5; i++)
monsterSummoningMediator->registerSummonedMonster(new Summoned<T>(this, "Summoned Monster"));
}
}Context
StackExchange Code Review Q#73567, answer score: 3
Revisions (0)
No revisions yet.