首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >运动物体坐标更新的责任链模式

运动物体坐标更新的责任链模式
EN

Code Review用户
提问于 2022-12-01 02:01:35
回答 1查看 56关注 0票数 1

PrimaryCoordinates是以米为单位的坐标。SecondaryCoordinates是以分米为单位的坐标,原点位于主坐标。TertiaryCoordinates是以厘米为单位的坐标,原点在次要坐标处。对象的Position,视其大小而定,可以具有所有三种协调类型。那么假设一个Person拿着一个Gun,里面有一个Bullets,当这个人移动的时候,枪和子弹都会跟着移动。子弹会向枪询问新的坐标,而这反过来又会向人询问新的坐标。由于这条链,以及拥有Position的三个坐标系的复杂性,我决定在人移动时(或者如果只有枪移动的话),使用责任链来获得新的Position

代码语言:javascript
复制
#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();
}

输出:

代码语言:javascript
复制
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()方法将输出正确的坐标,尽管存储的值是不正确的。由于没有不断更新与观察者模式的坐标,程序的性能得到了极大的提高。我认为使用观察者模式也要复杂得多(接下来我将尝试并进行比较)。

EN

回答 1

Code Review用户

发布于 2022-12-01 10:35:03

虽然在这里使用一条责任链是个好主意,但您选择的实现却不是。最重要的问题是它没有规模。考虑添加一个Car,以及能够进入汽车和驾驶的Person。现在添加一个Ferry,并将Car驱动到Ferry上。使用您的方法,您将需要QuaternaryCoordinatesQuinaryCoordinatesPosition将增长,处理所有可能的嵌套级别的复杂性将成倍增长。

相反,考虑在每个实体中只存储一个Coordinate,如果一个实体被另一个实体持有,那么第一个实体的坐标将被视为相对于持有者的坐标。所以:

代码语言:javascript
复制
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(),以指示它是一个操作,而不是查询。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/281601

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档