Sign in to follow this  

Proper OO method for condensing code segment

This topic is 4835 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Ok, while I was writing a Board class I came across a bit of code which I have always struggled with optimizing.
/**
 * Movement functions with auto 
 * edge detection.
 */
function Board_SouthWest( x,y ) {
 var toRet = index(x,y);
 if( !Board_is_Edge(x,y) ) {
  toRet = Board_index(x-[1],y+[1]);
 }
 return toRet;
}
function Board_NorthWest( x, y) {
 var toRet = index(x,y);
 if( !Board_is_Edge(x,y) ) {
  toRet = Board_index(x-[1],y-[1]);
 }
 return toRet;
}
function Board_North( x, y ) {
 var toRet = index(x,y);
 if( !Board_is_Edge(x,y) ) {
  toRet = Board_index(x,y-[1]);
 }
 return toRet;
} 
function Board_South( x, y ) {
 var toRet = index(x,y);
 if( !Board_is_Edge(x,y) ) {
  toRet = Board_index(x,y+[1]);
 }
 return toRet;
}
function Board_East( x, y ) {
 var toRet = index(x,y);
 if( !Board_is_Edge(x,y) ) {
  toRet = Board_index(x+[1],y);
 }
 return toRet;
}
function Board_West( x, y ) {
 var toRet = index(x,y);
 if( !Board_is_Edge(x,y) ) {
  toRet = Board_index(x-[1],y);
 }
 return toRet;
}
function Board_NorthEast( x, y ) {
 var toRet = index(x,y);
 if( !Board_is_Edge(x,y) ) {
  toRet = Board_index(x+[1],y-[1]);
 }
 return toRet;
}
function Board_southEast( x, y ) {
 var toRet = index(x,y);
 if( !Board_is_Edge(x,y) ) {
  toRet = Board_index(x+[1],y+[1]);
 }
 return toRet;
}

This is writen in javascript and I have many other such implementation in other languages including java, c/c++, etc... I have one implementation where I used a mathmatical parameter to one function which allow me to use a switch statement with one additional function to perform all of these movements. I am still struggling with the exact OO method for implementing this in a cleaner more optimized way. If anyone can propose a solution that would be great.

Share this post


Link to post
Share on other sites
So I've taken a stab at you're sample code. I kinda feel like i'm designing in the dark because there's no code showing how this object would be used, but I through something together based on what I thought was the intention. I'll say upfront, this isn't incredibly "object oriented" or anything. It's a simple solution that directly addresses the question at hand. I suppose you could get all crazy and overcomplicated with this, but given the paramteres of the question, I didn't see a reason to it.

I'd like some comments on my implementation. I'm curious as to what everyone feels are the pros and cons.

I didn't try compiling this yet, it should work. Regardless, the intent is clear:
class Board
{
public:
enum BoardDirection
{
BD_NORTH = 1,
BD_EAST = 2,
BD_SOUTH = 4,
BD_WEST = 8
};

int Index(const unsigned int x, const unsigned int y);

int IndexInDirection(const unsigned int startX, const unsigned int startY, const BoardDirection direction)
{
unsigned int destinationX = startX;
unsigned int destinationY = startY;

CalculatePosition(direction, destinationX, destinationY);
ClampValue(destinationX, 0, m_Width);
ClampValue(destinationY, 0, m_Height);

return Index(destinationX, destinationY);
}

private:
void CalculatePosition(const BoardDirection direction, unsigned int& x, unsigned int& y)
{
if(BD_NORTH & direction)
{
--destinationY;
}
else if(BD_SOUTH & direction)
{
++destinationY;
}

if(BD_EAST & direction)
{
--destinationX;
}
else if(BD_WEST & direction)
{
++destinationX;
}
}

void ClampValue(unsigned int& value, int begin, int end)
{
if(value < begin)
value = begin;
else if(value >= end)
value = end - 1;
}

unsigned int m_Width;
unsigned int m_Height;
};

Share this post


Link to post
Share on other sites
That works, but is over-engineered for my taste. When I have to do something like this I usually prefer to have the client code provide delta values directly; if the client code has to determine a direction, it is often easier to calculate those values directly, rather than translate to a code value (and then have to translate back again).

The original code has a problem in that Board_is_Edge doesn't know *which* edge to check against. So if you reach *any* edge of the board, suddenly you can't move *at all*. Also, OO isn't a magic bullet; the thing you should be worried about here is getting rid of all that awful duplication.

On the other hand, having a Board class does probably make some sense for whatever your application is, and provides a convenient place to store whatever information is needed to calculate the equivalent of "Board_index".

Thus:


// I hope you don't mind that I am writing Java, and according
// to a rather different coding standard :)
public class Board {
// I am also going to fill in a couple of the other details
// that you might expect to see in the class
private Square[][] grid;
// Java can tell us the height and width.

// Assuming the player has some location in the grid, which
// is special by virtue of the fact that the player is there
private int[] playerCoords;

// insert constructors here.

public Square squareAt(int x, int y) {
return squareInDirection(x, y, 0, 0);
}

// The exact organization of implementation vs interface
// is a language-specific issue. In Java, everything needs
// to be inline in your classes; the cultural assumption is
// that if you don't trust people, you give them your .class
// file and the javadoc. In C++ there are other considerations
// to worry about like the Pimpl idiom...
public Square squareInDirection(int x, int y, int dx, int dy) {
int[] target = clip(x+dx, y+dy);
return grid[target[0]][target[1]];
// There are various reasons why you might want to return
// a copy instead, but I find it more Java-idiomatic to
// embrace the way that objects are aliased. Of course,
// I don't really have large-project experience...
}

public Square playerSquare() {
return grid[playerCoords[0]][playerCoords[1]];
}

public void movePlayer(int dx, int dy) {
playerCoords = clip(playerCoords[0]+dx, playerCoords[1]+dy);
}

private static int[] clip(int x, int y) {
int[] result = new int {x, y};
int h = grid.length;
int w = grid[0].length;
if (result[0] < 0) result[0] = 0;
if (result[0] >= h) result[0] = h - 1;
if (result[1] < 0) result[1] = 0;
if (result[1] <= w) result[1] = w - 1;
return result;
}
}




Although, there seems to be a common theme now of using int[]'s to represent coordinates. Time to make a new class to represent a coordinate:


public class Coordinate {
private int w;
private int h;
public Coordinate(int w, int h) {
this.w = w; this.h = h; // C++ got this one right. Sigh.
}
public void clipTo(Coordinate mins, Coordinate maxes) {
if (w < mins.w) w = mins.w;
if (w > maxes.w) w = maxes.w;
if (h < mins.h) h = mins.h;
if (h > maxes.h) h = maxes.h;
}
public void add(Coordinate other) {
w += other.w; h += other.h;
}
public Coordinate sum(Coordinate other) {
return new Coordinate(w + other.w, h + other.h);
}
// This is perhaps cleaner than having public data or using
// accessors... in Java 1.5, we can avoid casting via generics.
public Object access(Object[][] grid) {
return grid[w][h];
}
}

// And now the Board class becomes simpler:
public class Board { // in a separate file
private Square[][] grid;
private Coordinate bottomRight;
private Coordinate playerLoc;

private static Coordinate origin = new Coordinate(0,0);
// Or maybe the Coordinate class will implement some Factory
// pattern instead...

public Board(int x, int y) {
grid = new Square[x][y];
// initialize further
bottomRight = new Coordinate(x-1, y-1);
}

public Square squareAt(Coordinate loc) {
return squareInDirection(loc, origin);
}

public Square squareInDirection(Coordinate loc, Coordinate direction) {
Coordinate target = loc.sum(direction);
target.clipTo(origin, bottomRight);
return (Square)(target.access(grid));
}

public Square playerSquare() {
return (Square)(playerLoc.access(grid));
}

public void movePlayer(Coordinate delta) {
playerLoc.add(delta);
playerLoc.clipTo(origin, bottomRight);
}
}




[Edited by - Zahlman on September 14, 2004 10:54:01 PM]

Share this post


Link to post
Share on other sites
Quote:
When I have to do something like this I usually prefer to have the client code provide delta values directly; if the client code has to determine a direction, it is often easier to calculate those values directly, rather than translate to a code value (and then have to translate back again).

I'm curious what your rational is behind that statement.

The way I see it is that by having the client code specify named-constants for directions it:
--1) makes the code more explicit.
--2) reduces the duplication of the client code for calculating the direction.
--3) explicitly forces a direction of 1 unit per move (may or may not be important).

In all fairness, regarding my solution, I will say that requiring the use of bitflags for the direction is not 100% intuitive. But, being that we're all programmers here, it should be. The alternative solutions are provide four bools, one for each direction, or have one function for each direction. Both of those solutions seem to be overcomplicating the situation. Bitflags, while not very "high level" are certainly a viable, simple, solution. -- I'd like to hear some thoughts about that!

***************************************************************
warning: this next point I'm going to make is incredibly nitpicky, but it is the type of issue which, imo, can make or break the ease of use of an interface.

Zahlman please don't take offense to this, I'm not intending it as a personal attack. I'd like to hear your rationale about the points I'm making. This is a place for intelligent discussions!
***************************************************************
I can see the usefulness of your solution. However, in both 'squareInDirection' and 'movePlayer' you use a Coordinate for the direction, and the amount to move. The naming of the type for those functions leads me to two conflicting conclusions about their behavior:
--1) the coordinate is the absolute destination position.
--2) the coordinate encodes the relative distance and direction.

It just seems odd to me to be using a 'Coordinate' when you really want a Vector. The difference may be subtle, but one should not have to inspect the code of a method to understand how to use it.

The choice of clipTo for the coordinate method seems a bit misleading as well. Clipping refers to discarding something if it is outside of a range. However, the method is clamping the coordinate, not clipping it. clampInBounds(), or something more explicit, would be more intention revealing.

The constructor for Coordinate takes w and h. Are those short for width or height? I would have thought x and y.

The constructor for the Board takes x and y. Are those the coordinates of the origin? If so, why not just take a Coordinate instance? Or, are those the width and height?

Looking at the code, or using some common sense, tells me the answers to the the questions I posed. Others may not be so lucky, and this is a pretty trivial example.

** My point: Have Intention Revealing Interfaces

Share this post


Link to post
Share on other sites
I'll take a crack at it:



struct Interface
{
virtual ~Interface() {}
};

struct vector2D
{
vector2D(int x=0, int y=0) : x(x), y(y) {}
int x,y;
};

struct IPiece : virtual Interface
{
//Absolute location on the board
virtual vector2D Location()=0;

//Check if this piece can make this type of move
virtual bool ValidMove(vector2D relative_movement)=0;

//Move the piece (no error checking, just move it)
virtual void Move(vector2D position)=0;

//Put the piece in this exact spot, useful for board reset
virtual void Place(vector2D position)=0;

//List of valid (relative) movements this piece can make
//Useful for highlighting valid moves on the board
virtual const std::vector<vector2D>& ValidMoves()=0;
};

struct IBoard : virtual Interface
{
virtual void SetExtents(vector2D extents = vector2D(8,8))=0;
virtual bool ValidSquare(vector2D position)=0;

//Return the board location given a point of incident relative to the
// center of the board (assuming raster graphics, with 3D/vector graphics
// we'd need to use float's for the point-of-incident
virtual vector2D Pick(vector2D screen_coordinates)=0;
};

struct IPlayer : virtual Interface
{
virtual std::vector<IPiece*>& Pieces()=0;
};

struct IGame : virtual Interface
{
virtual bool EmptySquare(vector2D location)=0;
virtual IPlayer* Player(int index)=0;
virtual IBoard* Board()=0;

virtual IPiece* PickPiece(vector2D screen_click, int player)=0;
virtual bool TryMove(IPiece* picked_piece, vector2D picked_location)=0;
};


IPiece* Game::PickPiece(vector2D screen_click, int player)
{
//Find the square clicked
vector2D picked = this->Board()->Pick(screen_click);
if(!this->Board()->ValidSquare(picked))
return 0;

//See if there's a piece (of our player) on that square
std::vector<IPiece*>& pieces = this->Player(player)->Pieces();
for(size_t i=0; i<pieces.size(); ++i)
{
if(picked == pieces[i]->Location)
return pieces[i];
}
return 0;
}

//From the geometry engine, a piece has been picked as well as a potential move location
bool Game::TryMove(IPiece* picked_piece, vector2D picked_location)
{
if(!this->EmptySquare(picked_location))
return false;

vector2D movement = picked_piece->Location() - picked_location;
if(!picked_piece->ValidMove(movement))
return false;

if(this->Board()->ValidSquare(picked_location))
return false;

//Looks like a good move
picked_piece->Move(movement);
return true;
}


Share this post


Link to post
Share on other sites
Quote:
Original post by Clash
So I've taken a stab at you're sample code. I kinda feel like i'm designing in the dark because there's no code showing how this object would be used, but I through something together based on what I thought was the intention. I'll say upfront, this isn't incredibly "object oriented" or anything. It's a simple solution that directly addresses the question at hand. I suppose you could get all crazy and overcomplicated with this, but given the paramteres of the question, I didn't see a reason to it.

I'd like some comments on my implementation. I'm curious as to what everyone feels are the pros and cons.

I didn't try compiling this yet, it should work. Regardless, the intent is clear:
Does it fullfill the specification?

Share this post


Link to post
Share on other sites
Quote:
Does it fullfill the specification?

Based upon the original post, I believe so! Correct me if I misunderstood, but only the functions regarding movement, clamping to the edges, and getting the current index at a cell are used in the original post.

Also, I'd like to hear some responses to the points I made in my second post.

Thanks :D

[Edited by - Clash on September 16, 2004 12:18:10 AM]

Share this post


Link to post
Share on other sites
Oops, I didn't see 5minute gaming's post. I have my filter set too high and saw your post first and thought it was just some random code you wanted people to comment on. It seemed strange.

Share this post


Link to post
Share on other sites
First of all I would like to thank all of you for responding with very interesting implementations. Clash's implementation is a low-level solution. One that I would have tried but I was looking to get ride of that awefully huge if...else or a switch statment. I was looking for high cohesion and low coupling of the functionality.

Zahlman's implementation seems to be what I would be after if I was not using javascript but seen as how I am and I don't want a very large system I solved the problem pretty much the same way. Except for the different classes an such.

I was originally looking for a algorithm to find all the tiles on a horizantal line from the given x and y coordinates as well as all the tiles on a negative diagnol slope and a positive slope as well as a vertical slope. The reason being is that I wanted to be able to compare each index within those on the line but I didn't need to compare all the indexes of the entire board if you follow me.

I developed a recursive method which could be simplified if you wanted to do single step movements but I'll post it simply because it was my solution.


get_Tiles_In_Direction( x, y, dx, dy, lst ) {

lst.add( Board_Index( x, y ) ); // add this tile

// if the next tile is not outside the board then
// move to the tile.
if( !isOverEdge( x+dx, y+dy ) ){
get_Tiles_In_Direction( x+dx, y+dy, dx, dy, lst );
}

}




There are many ways that this can be done but this function suits my purposes. For the exact OO method its completely up in the air. Thanks again for responding!

Share this post


Link to post
Share on other sites
Quote:
Original post by Clash
Quote:
When I have to do something like this I usually prefer to have the client code provide delta values directly; if the client code has to determine a direction, it is often easier to calculate those values directly, rather than translate to a code value (and then have to translate back again).

I'm curious what your rational is behind that statement.

The way I see it is that by having the client code specify named-constants for directions it:
--1) makes the code more explicit.
--2) reduces the duplication of the client code for calculating the direction.
--3) explicitly forces a direction of 1 unit per move (may or may not be important).

In all fairness, regarding my solution, I will say that requiring the use of bitflags for the direction is not 100% intuitive. But, being that we're all programmers here, it should be. The alternative solutions are provide four bools, one for each direction, or have one function for each direction. Both of those solutions seem to be overcomplicating the situation. Bitflags, while not very "high level" are certainly a viable, simple, solution. -- I'd like to hear some thoughts about that!


The problem is that with bitflags you can do silly things like NORTH|SOUTH, and also you have these ugly constants hanging around, and you have to do this kind of translation.

I like providing deltas because (a) as I mentioned, they're often easier to get at, and avoid back-and-forth converstion and (b) if later on you need to support other methods of movement, your task is quite simplified. Since you have to create the concept of "a direction" anyway, I'd rather not have to make some weird encoding.

Of course, I don't explicitly enforce the directions of movement, but that can be done easily enough - but I have to address the next point first...

Quote:
Zahlman please don't take offense to this, I'm not intending it as a personal attack. I'd like to hear your rationale about the points I'm making. This is a place for intelligent discussions!


No worries, I don't let things like this bother me even if they should, and here it shouldn't, because I know for a fact that I don't always do the best, OO-ideological thing - KISS is my #1 principle and I really only like object orientation to the extent that it helps me with that. Although I certainly can design extra stuff if it's warranted :)

Quote:
I can see the usefulness of your solution. However, in both 'squareInDirection' and 'movePlayer' you use a Coordinate for the direction, and the amount to move. The naming of the type for those functions leads me to two conflicting conclusions about their behavior:
--1) the coordinate is the absolute destination position.
--2) the coordinate encodes the relative distance and direction.

It just seems odd to me to be using a 'Coordinate' when you really want a Vector. The difference may be subtle, but one should not have to inspect the code of a method to understand how to use it.


Yes, this is the main ugly thing. It is common enough to confuse a point and a direction, and in the interest of writing up a simple example I didn't bother to make a distinction.

A Direction doesn't do nearly as much as a Coordinate, but it has the same sort of encoding and it seems a shame not to make these two classes related. Although I would certainly hear an argument for making them two completely different classes which coincidentally have x and y members; except that that makes it difficult to do the addition.

Quote:
The choice of clipTo for the coordinate method seems a bit misleading as well. Clipping refers to discarding something if it is outside of a range. However, the method is clamping the coordinate, not clipping it. clampInBounds(), or something more explicit, would be more intention revealing.


Er. Yeah, I guess so. Somehow I don't really like clampInBounds() as a name either, though. How about limitTo()?

Quote:
The constructor for Coordinate takes w and h. Are those short for width or height? I would have thought x and y.


They are, and I don't know why I made them w and h. :/

Quote:
The constructor for the Board takes x and y. Are those the coordinates of the origin? If so, why not just take a Coordinate instance? Or, are those the width and height?


Yeah. *Those* should have been the w and h. Brain fart. :s

Now then, presenting: some checking for valid Directions of movement...


public class Direction {
private int x;
private int y;
protected Direction(int x, int y) {
this.x = x; this.y = y;
}
// client classes cannot make base Directions, which ensures
// validity. But we allow construction of Coordinates.

// I am not going to bother with accessors here because
// it seems overkill for final, immutable members.
public static final Direction NW = new Direction(-1, -1);
public static final Direction N = new Direction( 0, -1);
public static final Direction NE = new Direction( 1, -1);
public static final Direction W = new Direction(-1, 0);
public static final Direction E = new Direction( 1, 0);
public static final Direction SW = new Direction(-1, 1);
public static final Direction S = new Direction( 0, 1);
public static final Direction SE = new Direction( 1, 1);
// You might find it useful to define the (0,0) direction too
// We could also implement a full Flyweight pattern to avoid
// creating all of these instances until they're used. But
// for only 8-9 of them...
}

public class Coordinate extends Direction {
public Coordinate(int x, int y) {
// I *think* it's valid to make this public...
super(x,y);
}
public void limitTo(Coordinate mins, Coordinate maxes) {
if (x < mins.x) x = mins.x;
if (x > maxes.x) x = maxes.x;
if (y < mins.y) y = mins.y;
if (y > maxes.y) y = maxes.y;
}
// PROBLEM: We are not protected from adding other
// Coordinates - or other Direction subclasses.
// To do that cleanly would mean inverting the hierarchy -
// but then Coordinates would be addable to other Coordinates,
// and that's really bad because our Coordinate objects
// are supposed to be immutable.
// So that would motivate the "two separate classes" approach,
// but then you have to play games in order to do the actual
// addition without breaking encapsulation. AARGH.
public void add(Direction other) {
x += other.x; y += other.y;
}
public Coordinate sum(Direction other) {
return new Coordinate(x + other.x, y + other.y);
}
// This is perhaps cleaner than having public data or using
// accessors... in Java 1.5, we can avoid casting via generics.
public Object access(Object[][] grid) {
return grid[x][y];
}
}

public class Board {
private Square[][] grid;
private Coordinate bottomRight;
private Coordinate playerLoc;

private static Coordinate origin = new Coordinate(0,0);
// Or maybe the Coordinate class will implement some Factory
// pattern instead...

// As pointed out, we should have width/height here, and
// x/y in the actual coordinates/directions. Meh.
public Board(int w, int h) {
grid = new Square[w][h];
// initialize further
bottomRight = new Coordinate(w-1, h-1);
}

public Square squareAt(Coordinate loc) {
return squareInDirection(loc, origin);
}

public Square squareInDirection(Coordinate loc, Direction d) {
Coordinate target = loc.sum(d);
target.limitTo(origin, bottomRight);
return (Square)(target.access(grid));
}

public Square playerSquare() {
return (Square)(playerLoc.access(grid));
}

public void movePlayer(Direction d) {
playerLoc.add(d);
playerLoc.limitTo(origin, bottomRight);
}
}



Better? :)

Share this post


Link to post
Share on other sites
Well, that is a bit clearer. However I don't see why you made a direction class and a coordinate class or why you would make a coordinate a direction. I would think that coordinates can have a direction and thats a 'has a' relationships not a 'is a' relationship. Or in effect you could have it such that a Direction is a mathematical vector where it 'has a' start and 'has a' end coordinates. Therefore neither would be polymorphically coupled like you have them they would be coupled rather by a dependancy.
But then you would be allowed to derived different types of directions and even different types of coordinates.

Share this post


Link to post
Share on other sites
Zahlman, I really like the idea of having the direction be an object. Encoding the delta pairs in named constants is much better! I initially thought you meant the user specified movement as deltas. Good stuff.

As far as the "direction vs. coordinate" problem goes, it is certainly an interesting one. The humorous thing about it is that in my current engine, we have 2D and 3D Vectors. There is no differentiation between a direction and a coordinate. I've always thought it was a bit odd, but fortunately for us it hasn't been a huge issue (it's not a big leap from vector to coordinate). IMO, the peculiarity of a proper solution comes from the fact that a direction and a coordinate are the same data, just interpreted differently.

It's difficult coming up with an argument for how to actually make the coordinate and vector seperate classes. I would almost think that coordinate be the base class, storing only the x and y positions. Then, have direction extend that and add functions for dealing with vectors such as length, angle, summation, etc.

This all seems a bit overengineered to me. Perhaps a vector class to represent coordinates and directions is appropriate after all? Maybe a simple typedef of Vector to Coordinate would suffice?

Share this post


Link to post
Share on other sites
The inheritance definitely is a hack, to expose the Direction internals only to Coordinates.

There is another option: make both these classes have-a "XYPair", or some other silly-named thing like that, where the data members are public but the class itself is not. Then put XYPair, Coordinate and Direction in a package together, without anything else inside.

That's Java-specific, but the net effect is that outside classes do not know of the existance of XYPair, so they will not be able to get at any of those internals, although Coordinate and Direction are able to share them. Wow, I actually almost miss the 'friend' keyword now.

(You could also make the XYPair a public class with package-level constructor, so that only Directions and Coordinates can *make* XYPairs, which should still give you enough security. In that setup, you might also put factory methods into the XYPair class, and hide the Direction/Coordinate constructors.)

Once you've done that you can also prevent Coordinates from adding to other Coordinates (requiring a Direction), since Coordinate no longer inherits from Direction.

I agree that this is all over-engineered and I never write stuff like this, but these are the kinds of lengths you end up going to if you put too much faith in OO dogma :)

Unfortunately Java lacks 'typedefs' :\ Of course, my choice of Java to present example code is rather arbitrary.

Share this post


Link to post
Share on other sites
I may be a little confused but isn't the only purpose of data-hiding to prevent something from modifying the invariant of an object?

Why don't you just produce a datatype that holds the vector data and write the ruitines required to perform operations like comparing and adding/subracting, ..., on the vector structure.


struct vector {
datatype x;
datatype y;
datatype z;
// datatype can be any type you require or even a template parm!
};


You then write classes like Coordinate and Direction which provide extended operations for working with vectors assuming they have themselves little need for making the vectors they contain part of their invariant.

Finally to maintain the Board object's invariant you simply use all of the utilities you've just written to aid in writing a few simple checks (like to make sure that the translation vector is no more than x+-1 or a y+-1 translation).

Such a solution is still as far as I know Object Oriented since I'm unaware of OO sugesting that data must be C++ primitives held in a class that protects such primatives as an invariant instead of simply producing a compound data type that is more suitable for this problem.

Share this post


Link to post
Share on other sites
Well that brings up the point of whether you really need two classes. You could just make them both one class called a VectorCoordinate which can be added to each other resulting in a new VectorCoordinate thus the direction or the movement of one tile or object (whatever) would be in terms of distance between your current location and the amount which you desire to deviate from that coordinate.

Thus the direction coordinate becomes a relative posistion to your current location.

Let me illustrate:

public class CoordinateVector {

private x=0, y=0;

public CoordinateVector( int x, int y ) {
this.x = x;
this.y = y;
}

public CoordinateVector translate( CoordinateVector dest ) {
return( new CoordinateVector( this.x + dest.x, this.y + dest.y ) );
}

public void moveTo( CoordinateVector dest ) {
this.x = dest.x;
this.y = dest.y;
}

}




This is simple and it can be added to do do other functionality such as mirroring over x and y axis if need be and you would want to keep the Board functionality asside from this classes functionality there is a cohesion issue if you combine them. This is very similar to javas Point class and if I was using java I would have used it instead of writing my own function. But I was using javascript and I was thinking too much along the lines of Arrays than Cartisian Planes.

Now if you wanted to expand this and create a Board using either this class or java.awt.Point or even java.awt.geom.Point2D how would you do it?

Share this post


Link to post
Share on other sites

This topic is 4835 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this