When putting on a piece of clothing, we need to check for any clothing that is already on the relevant body parts(s). The possible plural means that we should use the Composite Pattern for the body parts, and then check if the existing clothes must be removed or not. Some clothes can go over other clothes, while some cannot. Some covered body parts are subsets, supersets, or intersect with the body part(s) being covered by the clothing currently being handled. For example, if one is wearing a skirt and then desires to put on pants. Should the skirt be removed first? How can we tell? How do we put on shoulder pads if someone is already wearing a coat over a sweater over a tank top? If someone is wearing a gown and then wants to put on pants, what should be done with the gown which intersects with the pants at the hips? Can you put on pants if you are already wearing snowpants? A coat should be allowed to cover anything, right? But what if you are already wearing a space suit? Here is how I handled this, but I do mention some logical bugs that remain and I cannot seem to find a way out of right now. Or maybe the entire design must change.
#include <iostream>
#include <list>
#include <set>
#include <memory>
class Clothing {
std::string name;
public:
Clothing (const std::string& n) : name(n) { }
virtual bool canBeWornOver() const { return true; } // Any article of clothing can be worn over this.
virtual bool canWearOverAnything() const { return false; } // Value true means this can wear over anything except for clothes with absolutelyCannotBeWornOver() == false.
virtual bool absolutelyCannotBeWornOver() const { return false; } // 'canBeWornOver()' alone will not suffice, else Coat can be worn over a SpaceSuit, for example.
std::string getName() const { return name; }
void removeFromBodyPart() { std::cout << name << " has been removed and dropped to the floor.\n"; }
};
class BodyComponent {
std::list<BodyComponent*> children; // For example, Legs has children Thighs, Shins, Knees, Hips, Feet (which itself has Toes, Heel, Sole as children).
BodyComponent* parent;
std::list<Clothing*> clothingWorn; // For example, Foot can have two layers of socks and shoes on.
std::string name;
public:
BodyComponent (const std::string& n) : name(n) { }
std::string getName() const { return name; }
void add (BodyComponent* component) {
children.push_back(component);
component->parent = this;
}
void addWithoutChangingParent (BodyComponent* component) { children.push_back(component); }
bool attemptToPutOn (Clothing* clothing) {
std::set<Clothing*> alreadyRefusedToRemove;
std::cout << "\nAttempting to put on " << clothing->getName() << ":\n";
if (checkIfWillBePutOn(clothing, alreadyRefusedToRemove)) {
clothingWorn.push_back(clothing);
// clothing->setIsWornBy(this);
std::cout << clothing->getName() << " has been put on " << name << ".\n";
return true;
}
else {
std::cout << clothing->getName() << " was not put on " << name << ".\n";
return false;
}
}
bool checkIfWillBePutOn (Clothing* clothing, std::set<Clothing*>& alreadyRefusedToRemove) {
// First check the children, e.g. before putting on pants decide if the shin guards should be removed first. Note we assume that 'clothing' will be worn over any clothing on the children body parts.
std::list<Clothing*>::const_iterator it = clothingWorn.cbegin();
while (it != clothingWorn.cend()) {
if (alreadyRefusedToRemove.count(*it) != 0) {
++it; // No need to ask again if the user has already refused to remove the clothing *it. Note that '(*it)->canBeWornOver()' must be true if *it is already in alreadyRefusedToRemove.
continue;
}
char yesNo;
std::cout << (*it)->getName() << " is covering " << name << ".\n";
if ( (*it)->canBeWornOver() || (clothing->canWearOverAnything() && !(*it)->absolutelyCannotBeWornOver() ) ) {
// 'if ( (*it)->canBeWornOver() || clothing->canWearOverAnything() )' is not good enough, as it would allow a Coat to be worn over a SpaceSuit.
std::cout << "But " << clothing->getName() << " can be worn on top. Do you wish remove it anyway? (y/n) ";
std::cin >> yesNo;
if (yesNo == 'y') {
(*it)->removeFromBodyPart();
it = clothingWorn.erase(it);
}
else
alreadyRefusedToRemove.insert(*it++);
}
else {
std::cout << "It must be removed first. Do you wish to do so? (y/n) ";
std::cin >> yesNo;
if (yesNo == 'n')
return false; // 'clothing' will not be put on at all. Nothing left to do.
(*it)->removeFromBodyPart();
it = clothingWorn.erase(it); // Removing element while iterating through must be done this way.
}
}
// Next check the parent, if any. Note we assume that 'clothing' will be worn under (not over) any clothing on 'parent'. This changes the above algorithm a bit.
if (!willRemoveNecessaryParentClothing(clothing, alreadyRefusedToRemove))
return false;
// Repeat recursively with all the children, if any (Composite Pattern).
for (BodyComponent* component : children)
if (!component->checkIfWillBePutOn(clothing, alreadyRefusedToRemove))
return false; // One child failing to remove whatever is necessary means that 'clothing' will not be put on.
return true; // All children (if any) succeeded, so the entire operation succeeds, and 'clothing' will be put on.
}
private:
bool willRemoveNecessaryParentClothing (Clothing* clothing, std::set<Clothing*>& alreadyRefusedToRemove) {
if (parent) {
std::list<Clothing*>::const_iterator it = parent->clothingWorn.cbegin();
while (it != parent->clothingWorn.cend()) {
if (alreadyRefusedToRemove.count(*it) != 0) {
++it; // No need to ask again if the user has already refused to remove the clothing *it. Note that '(*it)->canBeWornOver()' must be true if *it is already in alreadyRefusedToRemove.
continue;
}
char yesNo;
std::cout << (*it)->getName() << " is covering " << name << ".\n";
// Since we assume that 'clothing' will be worn under (not over) any clothing on 'parent', this if condition is changed a bit.
if ( clothing->canBeWornOver() || ((*it)->canWearOverAnything() && !clothing->absolutelyCannotBeWornOver() ) ) {
std::cout << "But " << clothing->getName() << " can be worn underneath. Do you wish remove it anyway? (y/n) ";
std::cin >> yesNo;
if (yesNo == 'y') {
(*it)->removeFromBodyPart();
it = clothingWorn.erase(it);
}
else
alreadyRefusedToRemove.insert(*it++);
}
else {
std::cout << "It must be removed first. Do you wish to do so? (y/n) ";
std::cin >> yesNo;
if (yesNo == 'n')
return false; // 'clothing' will not be put on at all. Nothing left to do.
(*it)->removeFromBodyPart();
it = clothingWorn.erase(it); // Removing element while iterating through must be done this way.
}
}
return parent->willRemoveNecessaryParentClothing(clothing, alreadyRefusedToRemove); // Repeat recursively if 'parent' also has a parent.
}
return true;
}
};
class Legs : public BodyComponent { public: using BodyComponent::BodyComponent; };
class LegsExceptFeet : public BodyComponent { public: using BodyComponent::BodyComponent; };
class Shins : public BodyComponent { public: using BodyComponent::BodyComponent; };
class Hips : public BodyComponent { public: using BodyComponent::BodyComponent; };
class Thighs : public BodyComponent { public: using BodyComponent::BodyComponent; };
class Torso : public BodyComponent { public: using BodyComponent::BodyComponent; };
class TorsoAndLegsExceptFeet : public BodyComponent { public: using BodyComponent::BodyComponent; };
class EntireBody : public BodyComponent { public: using BodyComponent::BodyComponent; };
class Pants : public Clothing {
public:
using Clothing::Clothing;
};
class SnowPants : public Pants {
public:
using Pants::Pants;
virtual bool canBeWornOver() const override { return false; } // Cannot have something, likes Pants, worn on top because it is too big.
};
class Underwear : public Clothing { public: using Clothing::Clothing; };
class ShinGuards : public Clothing { public: using Clothing::Clothing; };
class Coat : public Clothing {
public: using Clothing::Clothing;
virtual bool canBeWornOver() const override { return false; }
virtual bool canWearOverAnything() const override { return true; }
};
class FullLengthCoat : public Coat {
using Coat::Coat;
};
class ThreeQuarterCoat : public Coat {
using Coat::Coat;
};
class SpaceSuit : public Clothing {
public: using Clothing::Clothing;
virtual bool canBeWornOver() const override { return false; }
virtual bool absolutelyCannotBeWornOver() const override { return true; }
virtual bool canWearOverAnything() const override { return true; }
};
class Person {
struct Anatomy {
Person* owner;
EntireBody* entireBody = new EntireBody(owner->name + "'s entire body");
Legs* legs = new Legs(owner->name + "'s legs"); // Let this be for both legs for simplicity.
LegsExceptFeet* legsExceptFeet = new LegsExceptFeet(owner->name + "'s legs except feet");
Shins* shins = new Shins(owner->name + "'s shins"); // Let this be for both shins for simplicity.
Thighs* thighs = new Thighs(owner->name + "'s thighs"); // Let this be for both thighs for simplicity.
Hips* hips = new Hips(owner->name + "'s hips");
TorsoAndLegsExceptFeet* torsoAndLegsExceptFeet = new TorsoAndLegsExceptFeet(owner->name + "'s torso and legs");
Torso* torso = new Torso(owner->name + "'s torso");
Anatomy (Person* person) : owner(person) {
legsExceptFeet->add(shins);
legsExceptFeet->add(thighs);
legsExceptFeet->add(hips);
torsoAndLegsExceptFeet->add(legsExceptFeet);
torsoAndLegsExceptFeet->add(torso);
// legs->add(legsExceptFeet); legs-->add(feet);
// feet->add(heel); feet->add(toes);
// torso->add(arms);
// arms->add(hands);
// hands->add(palm); hands->add(fingers); etc...
entireBody->add(torsoAndLegsExceptFeet);
}
};
std::string name;
Anatomy* anatomy;
public:
Person (const std::string& n) : name(n), anatomy(new Anatomy(this)) { }
bool attemptToPutOn (Pants* pants) { return anatomy->legsExceptFeet->attemptToPutOn(pants); }
bool attemptToPutOn (Underwear* underwear) { return anatomy->hips->attemptToPutOn(underwear); }
bool attemptToPutOn (ShinGuards* shinGuards) { return anatomy->shins->attemptToPutOn(shinGuards); }
bool attemptToPutOn (FullLengthCoat* coat) { return anatomy->torsoAndLegsExceptFeet->attemptToPutOn(coat); }
bool attemptToPutOn (SpaceSuit* spaceSuit) { return anatomy->entireBody->attemptToPutOn(spaceSuit); }
bool attemptToPutOn (ThreeQuarterCoat* threeQuarterCoat) {
BodyComponent* torsoHipsThighs = new BodyComponent(name + "'s body under part under " + threeQuarterCoat->getName()); // Using a smart pointer causes a crash down the road that I don't understand.
torsoHipsThighs->addWithoutChangingParent(anatomy->torso);
torsoHipsThighs->addWithoutChangingParent(anatomy->hips);
torsoHipsThighs->addWithoutChangingParent(anatomy->thighs);
return torsoHipsThighs->attemptToPutOn(threeQuarterCoat); // But how to reuse 'torsoHipsThighs' if, for example, another ThreeQuarterCoat is to be put on afterwards? Store it in 'anatomy'?
}
};
int main() {
Person person("Bob");
Pants pants("pants");
SnowPants snowPants("snow pants");
Underwear underwear("underwear");
ShinGuards shinGuards("shin guards");
FullLengthCoat fullLengthCoat("full length coat");
SpaceSuit spaceSuit("space suit");
ThreeQuarterCoat threeQuarterCoat("red coat that covers down to knees"), threeQuarterCoat2("blue coat that covers down to knees");
person.attemptToPutOn(&underwear);
person.attemptToPutOn(&shinGuards);
person.attemptToPutOn(&snowPants);
person.attemptToPutOn(&threeQuarterCoat);
person.attemptToPutOn(&threeQuarterCoat2);
person.attemptToPutOn(&pants);
person.attemptToPutOn(&snowPants);
person.attemptToPutOn(&fullLengthCoat); // fullLengthCoat does not check the threeQuarterCoat worn because 'torsoHipsThighs' is not being checked.
person.attemptToPutOn(&spaceSuit);
person.attemptToPutOn(&spaceSuit);
person.attemptToPutOn(&fullLengthCoat);
}
Sample output:
Attempting to put on underwear:
underwear has been put on Bob's hips.
Attempting to put on shin guards:
shin guards has been put on Bob's shins.
Attempting to put on snow pants:
shin guards is covering Bob's shins.
But snow pants can be worn on top. Do you wish remove it anyway? (y/n) n
underwear is covering Bob's hips.
But snow pants can be worn on top. Do you wish remove it anyway? (y/n) n
snow pants has been put on Bob's legs except feet.
Attempting to put on red coat that covers down to knees:
underwear is covering Bob's hips.
But red coat that covers down to knees can be worn on top. Do you wish remove it anyway? (y/n) n
snow pants is covering Bob's hips.
It must be removed first. Do you wish to do so? (y/n) y
snow pants has been removed and dropped to the floor.
red coat that covers down to knees has been put on Bob's body under part under red coat that covers down to knees.
Attempting to put on blue coat that covers down to knees:
underwear is covering Bob's hips.
But blue coat that covers down to knees can be worn on top. Do you wish remove it anyway? (y/n) n
blue coat that covers down to knees has been put on Bob's body under part under blue coat that covers down to knees. // *** Bug. The already worn ThreeQuarterCoat is not checked because the previous 'torsoHipsThighs' is not being used anymore.
Attempting to put on pants:
shin guards is covering Bob's shins.
But pants can be worn on top. Do you wish remove it anyway? (y/n) n
underwear is covering Bob's hips.
But pants can be worn on top. Do you wish remove it anyway? (y/n) n
pants has been put on Bob's legs except feet.
Attempting to put on snow pants:
pants is covering Bob's legs except feet.
But snow pants can be worn on top. Do you wish remove it anyway? (y/n) n
shin guards is covering Bob's shins.
But snow pants can be worn on top. Do you wish remove it anyway? (y/n) n
underwear is covering Bob's hips.
But snow pants can be worn on top. Do you wish remove it anyway? (y/n) n
snow pants has been put on Bob's legs except feet.
Attempting to put on full length coat:
pants is covering Bob's legs except feet.
But full length coat can be worn on top. Do you wish remove it anyway? (y/n) n
snow pants is covering Bob's legs except feet.
But full length coat can be worn on top. Do you wish remove it anyway? (y/n) n
shin guards is covering Bob's shins.
But full length coat can be worn on top. Do you wish remove it anyway? (y/n) n
underwear is covering Bob's hips.
But full length coat can be worn on top. Do you wish remove it anyway? (y/n) n
full length coat has been put on Bob's torso and legs.
Attempting to put on space suit:
full length coat is covering Bob's torso and legs.
But space suit can be worn on top. Do you wish remove it anyway? (y/n) n
pants is covering Bob's legs except feet.
But space suit can be worn on top. Do you wish remove it anyway? (y/n) n
snow pants is covering Bob's legs except feet.
But space suit can be worn on top. Do you wish remove it anyway? (y/n) n
shin guards is covering Bob's shins.
But space suit can be worn on top. Do you wish remove it anyway? (y/n) y
shin guards has been removed and dropped to the floor.
underwear is covering Bob's hips.
But space suit can be worn on top. Do you wish remove it anyway? (y/n) n
space suit has been put on Bob's entire body.
Attempting to put on space suit:
space suit is covering Bob's entire body.
It must be removed first. Do you wish to do so? (y/n) n
space suit was not put on Bob's entire body.
Attempting to put on full length coat:
full length coat is covering Bob's torso and legs.
But full length coat can be worn on top. Do you wish remove it anyway? (y/n) n
space suit is covering Bob's torso and legs.
But full length coat can be worn underneath. Do you wish remove it anyway? (y/n) n
pants is covering Bob's legs except feet.
But full length coat can be worn on top. Do you wish remove it anyway? (y/n) n
snow pants is covering Bob's legs except feet.
But full length coat can be worn on top. Do you wish remove it anyway? (y/n) n
underwear is covering Bob's hips.
But full length coat can be worn on top. Do you wish remove it anyway? (y/n) n
full length coat has been put on Bob's torso and legs.
Note: To follow the Open-Closed Principle, all the Person::attemptToPutOn overloads should be moved out of Person and into some Visitor class. I just didn't bother to, in order to keep this more concise.