samedi 3 décembre 2016

read/write custom object (3D block) from a certain face and perspective (orientation)

What is a design pattern to enable a class (Block) to be viewed from different perspectives (Orientation)?

Example

I have a 3D block. It stores values at its 6 faces.

class Block{
    public: int faces[6];
    public: enum Direction{
        X_POSITIVE, X_NEGATIVE,
        Y_POSITIVE, Y_NEGATIVE,
        Z_POSITIVE, Z_NEGATIVE
    }
    public: int& getFace(Direction dir){
        return faces[dir];
    }
};

If I use Block's axis, I can assign a value of face easily:-

Block block;
block.getFace(Block::Direction::X_POSITIVE)=5;

However, in some part of program, I prefer to view Block from a certain orientation :-

//I want to rotate the block by "transform" then view as usual
Transform transform=Transform::create_rotateByX90Degree();
block.getFace(transform,Block::X_POSITIVE)=5;

As I have to query getFace() many times in rows, I have to pass transform every time.
This becomes messy:-

int& a = block.getFace(transform,Block::X_POSITIVE);//<-- "transform"
int& b = block.getFace(transform,Block::Y_POSITIVE);//<-- "transform" again (#)

Question

What is a design pattern to solve this repetitive code(#)?

My poor solution

I would create a Viewer.

Transform transform=Transform::create_rotateByX90Degree();
Viewer viewer = Block::createViewer(block,transform);
viewer.getFace(Block::X_POSITIVE);
viewer.getFace(Block::Y_POSITIVE);

It looks good, but has two certain maintainability problems :-

Every field must be encapsulated

If Block has many fields e.g. int faces2[6] , I will have to create additional functions in Viewer e.g. Viewer::getFace2().

More repetitive code for fields that also want Viewer

Furthermore, if Block has a more complex field, and the field's viewed value depend on transform again, I have to encapsulate the field too.

class Block{
    Member members[6];
}

Transform transform=Transform::create_rotateByX90Degree();
Viewer viewer = Block::createViewer(block,transform);
Member & member = viewer.getMember(Block::X_POSITIVE);

//Here, I get "member" that is inside "block", 
//    but I have to encapsulate "member" by another viewer again.

ViewerMember viewerD = ViewerMember::createViewerD(member ,transform);
ViewerMember.getSomething();

Aucun commentaire:

Enregistrer un commentaire