PrimaryCoordinates是以米为单位的坐标。SecondaryCoordinates是以分米为单位的坐标,原点位于主坐标。TertiaryCoordinates是以厘米为单位的坐标,原点在次要坐标处。对象的Position,视其大小而定,可以具有所有三种协调类型。那么假设一个Person拿着一个Gun,里面有一个Bullets,当这个人移动的时候,枪和子弹都会跟着移动。子弹会向枪询问新的坐标,而这反过来又会向人询问新的坐标。由于这条链,以及拥有Position的三个坐标系的复杂性,我决定在人移动时(或者如果只有枪移动的话),使用责任链来获得新的Position。
#include <iostream>
#include <string>
#include <list>
struct Coordinates {
int x, y, z;
Coordinates (int x, int y, int z) : x(x), y(y), z(z) { }
bool operator== (const Coordinates& other) const { return x == other.x && y == other.y && z == other.z; }
bool operator!= (const Coordinates& other) const { return !(*this == other); }
void print (std::ostream& os = std::cout) const {os << "(" << x << ", " << y << ", " << z << ")\n";}
};
struct PrimaryCoordinates : Coordinates { // Coordinates in meters.
using Coordinates::Coordinates;
} nonesuch = {-1,-1,-1};
struct SecondaryCoordinates : Coordinates { // Coordinates in decimeters, with the origin specified by PrimaryCoordinates.
using Coordinates::Coordinates;
} s_nonesuch = {-1,-1,-1};
struct TertiaryCoordinates : Coordinates { // Coordinates in centimeters, with the origin specified by SecondaryCoordinates.
using Coordinates::Coordinates;
} t_nonesuch = {-1,-1,-1};
struct Position {
PrimaryCoordinates primaryCoordinates;
SecondaryCoordinates secondaryCoordinates;
TertiaryCoordinates tertiaryCoordinates;
Position (const PrimaryCoordinates& p = nonesuch, const SecondaryCoordinates& s = s_nonesuch, TertiaryCoordinates t = t_nonesuch) : primaryCoordinates(p), secondaryCoordinates(s), tertiaryCoordinates(t) { }
void print (std::ostream& os = std::cout) const {
os << "Primary Coordinates = "; primaryCoordinates.print(os);
if (secondaryCoordinates != s_nonesuch) {
os << "Secondary Coordinates = "; secondaryCoordinates.print(os);
}
if (tertiaryCoordinates != t_nonesuch) {
os << "Tertiary Coordinates = "; tertiaryCoordinates.print(os);
}
}
};
class Entity {
Position position;
public:
virtual ~Entity() = default;
PrimaryCoordinates getPrimaryCoordinates() const { return position.primaryCoordinates; }
void setPrimaryCoordinates (const PrimaryCoordinates& p) { position.primaryCoordinates = p; }
void setPrimaryCoordinates (int x, int y, int z = 0) { position.primaryCoordinates = {x, y, z}; }
void setSecondaryCoordinates (int x, int y, int z = 0) { position.secondaryCoordinates = {x, y, z}; }
void setTertiaryCoordinates (int x, int y, int z = 0) { position.tertiaryCoordinates = {x, y, z}; }
void movesTo (int x, int y, int z = 0) { setPrimaryCoordinates(x, y, z); }
virtual Position findPosition (Position&) const { return position; }
Position getPosition() const { Position p; return findPosition(p); }
};
class ContainableEntity : public Entity { // A pillar, for example, is a NonContainableEntity, and thus should have no 'holder' data member.
protected:
Entity* holder = nullptr; // The 'next' pointer in the Chain of Responsibility pattern.
public:
void setHolder (Entity* h) { holder = h; } // The 'setNext()' method in the Chain of Responsibility pattern.
virtual Position findPosition (Position& p) const override { // The virtual 'handle()' method in the base 'Handler' class in the Chain of Responsibility pattern. Here the ContainableEntity class is the abstact 'Handler' base class.
if (holder)
return holder->findPosition(p);
p.primaryCoordinates = getPrimaryCoordinates();
return p;
}
};
class Item : public ContainableEntity { };
class Bullet : public Item { // A concrete Handler class (since it is a concrete derived class of Item) that could have its own 'handle() override'.
bool fired = false;
public:
void hasBeenFired() { fired = true; }
virtual Position findPosition (Position& p) const override { // This override is the 'handle() override' of a concrete derived Handler subtype in the Chain of Responsibility pattern, calling the base class' handle() method at the end.
if (!fired)
p.tertiaryCoordinates = {2, 2, 2};
else
p.tertiaryCoordinates = {2, 2, 0};
return ContainableEntity::findPosition(p);
}
};
class Gun : public Item { // A concrete Handler class (since it is a concrete derived class of Item) that could have its own 'handle() override'.
std::list<Bullet*> bullets;
public:
void addBullets (int amount) {
for (int i = 0; i < amount; i++) {
Bullet* bullet = new Bullet;
bullets.push_back (bullet);
bullet->setHolder(this);
}
}
void print (std::ostream& os = std::cout) const {
os << "Position of gun:\n";
getPosition().print();
os << "Coordinates of bullets:\n";
for (Bullet* x : bullets) {
os << "Bullet:\n";
x->getPosition().print();
}
}
Bullet* fire() {
Bullet* firedBullet = bullets.back();
firedBullet->movesTo(1000, 1000, 0);
firedBullet->setHolder(nullptr);
firedBullet->hasBeenFired();
bullets.pop_back();
return firedBullet;
}
virtual Position findPosition (Position& p) const override { // This override is the 'handle() override' of a concrete derived Handler subtype in the Chain of Responsibility pattern, calling the base class' handle() method at the end.
if (holder)
p.secondaryCoordinates = {3, 4, 8};
else
p.secondaryCoordinates = {3, 4, 0};
return ContainableEntity::findPosition(p);
}
};
class Inventory {
private:
class Person* holder = nullptr;
Gun* gun = nullptr;
public:
Inventory (Person* person) : holder(person) {}
Person* getHolder() const {return holder;}
void addItem (Gun*);
void removeGun();
Gun* getGun() const {return gun;}
void print (std::ostream& os = std::cout) const {
if (gun)
gun->print(os);
}
};
class Person : public ContainableEntity { // A Person could be contained in a (moving) car for example, which would be his 'holder'.
private:
std::string name;
Inventory* inventory;
public:
Person (const std::string& n) : name(n), inventory (new Inventory(this)) {setPrimaryCoordinates(10, 20, 0);}
std::string getName() const {return name;}
Inventory* getInventory() const {return inventory;}
void print (std::ostream& os = std::cout) const {
os << "Name: " << name << "\nCoordinates: ";
getPosition().print(os);
inventory->print(os);
}
void addItem (Gun* gun) const {inventory->addItem(gun);}
void removeGun() const {inventory->removeGun();}
Bullet* fireGun() const {
if (!getInventory()->getGun())
return nullptr;
return getInventory()->getGun()->fire();
}
};
void Inventory::addItem (Gun* g) {
gun = g;
gun->setHolder(holder);
}
void Inventory::removeGun() {
gun->setPrimaryCoordinates(holder->getPrimaryCoordinates());
gun->setHolder(nullptr);
gun = nullptr;
}
int main() {
Person* bob = new Person("Bob");
Gun* gun = new Gun;
gun->addBullets(3);
bob->addItem (gun);
bob->print();
std::cout << "\nBob fires his gun.\n";
Bullet* firedBullet = bob->fireGun();
std::cout << "Coordinates of fired bullet:\n";
firedBullet->getPosition().print();
bob->movesTo(50,40,0);
std::cout << "\nBob has moved to ";
bob->getPosition().print();
bob->print();
std::cout << "Coordinates of fired bullet:\n";
firedBullet->getPosition().print();
bob->removeGun();
std::cout << "\nBob has dropped his gun.\n";
bob->movesTo(100,200,0);
std::cout << "\nBob has moved to ";
bob->getPosition().print();
bob->print();
gun->print();
std::cout << "Coordinates of fired bullet:\n";
firedBullet->getPosition().print();
gun->movesTo(10,20,0);
std::cout << "\nThe gun has moved (somehow) to primary coordinates (10, 20, 0):\n";
gun->print();
std::cout << "Coordinates of fired bullet:\n";
firedBullet->getPosition().print();
}输出:
Name: Bob
Coordinates: Primary Coordinates = (10, 20, 0)
Position of gun:
Primary Coordinates = (10, 20, 0)
Secondary Coordinates = (3, 4, 8)
Coordinates of bullets:
Bullet:
Primary Coordinates = (10, 20, 0)
Secondary Coordinates = (3, 4, 8)
Tertiary Coordinates = (2, 2, 2)
Bullet:
Primary Coordinates = (10, 20, 0)
Secondary Coordinates = (3, 4, 8)
Tertiary Coordinates = (2, 2, 2)
Bullet:
Primary Coordinates = (10, 20, 0)
Secondary Coordinates = (3, 4, 8)
Tertiary Coordinates = (2, 2, 2)
Bob fires his gun.
Coordinates of fired bullet:
Primary Coordinates = (1000, 1000, 0)
Tertiary Coordinates = (2, 2, 0)
Bob has moved to Primary Coordinates = (50, 40, 0)
Name: Bob
Coordinates: Primary Coordinates = (50, 40, 0)
Position of gun:
Primary Coordinates = (50, 40, 0)
Secondary Coordinates = (3, 4, 8)
Coordinates of bullets:
Bullet:
Primary Coordinates = (50, 40, 0)
Secondary Coordinates = (3, 4, 8)
Tertiary Coordinates = (2, 2, 2)
Bullet:
Primary Coordinates = (50, 40, 0)
Secondary Coordinates = (3, 4, 8)
Tertiary Coordinates = (2, 2, 2)
Coordinates of fired bullet:
Primary Coordinates = (1000, 1000, 0)
Tertiary Coordinates = (2, 2, 0)
Bob has dropped his gun.
Bob has moved to Primary Coordinates = (100, 200, 0)
Name: Bob
Coordinates: Primary Coordinates = (100, 200, 0)
Position of gun:
Primary Coordinates = (50, 40, 0)
Secondary Coordinates = (3, 4, 0)
Coordinates of bullets:
Bullet:
Primary Coordinates = (50, 40, 0)
Secondary Coordinates = (3, 4, 0)
Tertiary Coordinates = (2, 2, 2)
Bullet:
Primary Coordinates = (50, 40, 0)
Secondary Coordinates = (3, 4, 0)
Tertiary Coordinates = (2, 2, 2)
Coordinates of fired bullet:
Primary Coordinates = (1000, 1000, 0)
Tertiary Coordinates = (2, 2, 0)
The gun has moved (somehow) to primary coordinates (10, 20, 0):
Position of gun:
Primary Coordinates = (10, 20, 0)
Secondary Coordinates = (3, 4, 0)
Coordinates of bullets:
Bullet:
Primary Coordinates = (10, 20, 0)
Secondary Coordinates = (3, 4, 0)
Tertiary Coordinates = (2, 2, 2)
Bullet:
Primary Coordinates = (10, 20, 0)
Secondary Coordinates = (3, 4, 0)
Tertiary Coordinates = (2, 2, 2)
Coordinates of fired bullet:
Primary Coordinates = (1000, 1000, 0)
Tertiary Coordinates = (2, 2, 0)但是,请注意,枪和子弹的坐标并没有用正确的值进行实际更新(正如观察者模式所做的那样)。但是我允许这种滑动的理由是,很少需要枪和子弹的坐标,当需要它们时(包括保存到txt文件),使用责任链模式的getPosition()方法将输出正确的坐标,尽管存储的值是不正确的。由于没有不断更新与观察者模式的坐标,程序的性能得到了极大的提高。我认为使用观察者模式也要复杂得多(接下来我将尝试并进行比较)。
发布于 2022-12-01 10:35:03
虽然在这里使用一条责任链是个好主意,但您选择的实现却不是。最重要的问题是它没有规模。考虑添加一个Car,以及能够进入汽车和驾驶的Person。现在添加一个Ferry,并将Car驱动到Ferry上。使用您的方法,您将需要QuaternaryCoordinates和QuinaryCoordinates,Position将增长,处理所有可能的嵌套级别的复杂性将成倍增长。
相反,考虑在每个实体中只存储一个Coordinate,如果一个实体被另一个实体持有,那么第一个实体的坐标将被视为相对于持有者的坐标。所以:
class Entity {
Coordinate position;
…
public:
virtual Position getPosition() const { return position; }
…
};
class ContainableEntity: public Entity {
…
public:
Position getPosition() const override {
if (holder) {
return holder->getPosition() + Entity::getPosition();
} else {
return Entity::getPosition();
}
}
};以上假设您向operator+()添加了一个Coordinate,并且所有坐标都使用相同的单位。
我还将删除findPosition(),而不更新getPosition()中的任何坐标。例如,在发射子弹时,只需更改hasBeenFired()内的坐标即可。该函数还应该重命名为fire(),以指示它是一个操作,而不是查询。
https://codereview.stackexchange.com/questions/281601
复制相似问题