Sheep and Herds in C++
From Wiki**3
The Problem (in Portuguese)
Um pastor tem muitas ovelhas de várias espécies (brancas de lã curta, pretas de lã comprida), das quais vende lã para para a indústria de tecelagem. O pastor vive numa região muito acidentada, onde o espaço para pastagens não abunda e ele viu-se obrigado a dividir as ovelhas em ebanhos, cada um dos quais ele leva a pastar para uma dada região (quase todos plataformas empoleiradas em penhascos).
Quando lhe perguntam como se consegue organizar, quando quer tosquiar todas as ovelhas, o pastor explica que tosquia as ovelhas recursivamente, de acordo com um método que viu num almanaque: considera o grupo de rebanhos que possui e assegura que tosquiar um grupo de rebanhos é como tosquiar cada rebanho. Se alguém faz a pergunta "como se tosquia um rebanho?", ele responde com um ar de quem tem mais que fazer que é como quem tosquia cada ovelha. O método funciona tão bem que ele está a pensar alugar terrenos nas montanhas vizinhas e levar para lá novos grupos de rebanhos. Quando quiser tosquiar tudo, apenas tem de tosquiar o grupo formado pelos grupos de rebanhos em cada zona.
1. Represente a organização de ovelhas, modelando cada espécie, assim como os grupos, considerando a abordagem do pastor à operação de tosquiar.
Passado algum tempo, o pastor comprou algumas cabras, por forma a aproveitar melhor os penhascos, já que as cabras podiam alcançar zonas inacessÃveis à s ovelhas. Para tornar o negócio rentável, o pastor passou a ordenhar as ovelhas e as cabras para obtenção de leite e posterior produção de queijo. O negócio parecia ideal, não fosse a complicação introduzida: os rebanhos tinham animais misturados e não se podiam aplicar as operações indiscriminadamente a todos os animais (não se podiam tosquiar as cabras, por exemplo).
Felizmente, o pastor conseguiu encontrar a solução no almanaque: só precisava de comprar algumas máquinas que fossem capazes de desempenhar cada tarefa: cada máquina visitaria cada grupo e cada rebanho e, dependendo da respectiva natureza, aplicaria a operação apropriada (ou ignoraria a entidade).
2. Represente a organização das máquinas de processamento pecuário e as alterações a fazer na modelação anterior.
Implementation of Part 1.
When implementing 1. we need to take into account the operation that is common to all entities: shearing (tosquiar). A simple implementation is obtained through inheritance as described by the Composite design pattern (as indicated by the shepherd's almanack). The top concept will represent something that can be sheared. We will call this concept "Shearable". Each sheep species will be a subclass of this concept, implementing it in some way (e.g. by modifying hair lenght). Groups will be aggregations of the base concept: in this way, we have great flexbility in distributing sheep among the herds, or herds among the various herd groups. The shearing operation will be carried out simply by iterating through each group's members and shearing it: if it is a sheep, then it will be sheared; if not, the iterative process will be repeated.
Class Shearable
The following are the contents of file Shearable.h (since it is a small class, it is completely defined in the header file). <cpp>
- ifndef __SHEARABLE_H__
- define __SHEARABLE_H__
class Shearable { public:
virtual ~Shearable() {} /** * Shearing operation. */ virtual void shear() = 0; /** * Hair growth: represents the passing of time. */ virtual void growWool() = 0; /** * */ virtual void add(Shearable *s) = 0;
};
- endif
</cpp>
Class Sheep
Class Sheep exist only to abstract features common to individual sheep, but not to herds. Wool length should be private and accessible only through a dedicated interface (we did not concern ourselves with this aspect, since we wanted to keep the example simple). The following are the contents of file Sheep.h. Note that, since sheep are shearable and might, in some contexts, be confused with herds, we need to provide some safety net for the add operation (in case it is wrongly called). <cpp>
- ifndef __SHEEP_H__
- define __SHEEP_H__
- include "Shearable.h"
class Sheep : public Shearable { protected:
int _woolLength; // should be private...
public:
/** * Initialize wool length. * @param woolLength the initial wool length. */ Sheep(int woolLength) : _woolLength(woolLength) {}
/** * Cannot add, but must do something... * @param s member to be added to a group (ignored) */ void add(Shearable *s) { throw "unsupported operation"; }
};
- endif
</cpp>
Class BlackSheep
The following are the contents of file BlackSheep.h. Since the wool is long, shearing keeps going until it is short. <cpp> ifndef __BLACKSHEEP_H__
- define __BLACKSHEEP_H__
- include <iostream>
- include "Sheep.h"
class BlackSheep : public Sheep { public:
/** * Initialization of black sheep with long wool. */ BlackSheep() : Sheep(100) {} /** * Shear sheep while wool is long. */ void shear() { std::cerr << "BlackSheep::shear" << std::endl; while (_woolLength > 10) _woolLength -= 10; } /** * Grow wool. */ void growWool() { std::cerr << "BlackSheep::growWhool" << std::endl; _woolLength += 100; }
};
- endif
</cpp>
Class WhiteSheep
The following are the contents of file BlackSheep.h. <cpp>
- ifndef __WHITESHEEP_H__
- define __WHITESHEEP_H__
- include <iostream>
- include "Sheep.h"
class WhiteSheep : public Sheep { public:
WhiteSheep() : Sheep(10) {} void shear() { std::cerr << "WhiteSheep::shear" << std::endl; if (_woolLength > 5) _woolLength -= 5; } void growWool() { std::cerr << "WhiteSheep::growWool" << std::endl; _woolLength += 1; }
};
- endif
</cpp>
The main function
The following is an example for testing the model: we create two sheep and three groups. <cpp>
- include "WhiteSheep.h"
- include "BlackSheep.h"
- include "Herd.h"
int main() {
Shearable *s1 = new WhiteSheep(); Shearable *s2 = new BlackSheep(); Shearable *g1 = new Herd(); Shearable *g2 = new Herd(); Shearable *g3 = new Herd();
g1->add(s1); g2->add(s2); g3->add(g1); g3->add(g2);
s1->shear(); s1->growWool(); s2->shear(); s2->growWool(); g1->shear(); g2->shear(); g3->growWool(); g3->shear();
delete s1; delete s2; delete g1; delete g2; delete g3;
} </cpp>