I'm working on computational geometry project. I have classes representing geometrical objects: Point, LineSegment and class, which performs calculations on those objects: Geometry. I'm confused with the Law of Demeter and "MethodWithPointAndLineSegment
".
class Point
{
public int X { get; set; }
public int Y { get; set; }
...
}
class LineSegment
{
public Point InitialPoint { get; set; }
public Point TerminalPoint { get; set; }
...
}
class Geometry
{
private ... MethodWithThreePoints(Point p1, Point p2, Point p3)
{
// accessing X and Y properties of passed points
...
}
public ... MethodWithPointAndLineSegment(Point p1, LineSegment segment)
{
MethodWithThreePoints(p1, segment.InitialPoint, segment.TerminalPoint);
...
}
}
My question is: does MethodWithPointAndLineSegment
violates the Law od Demeter? I suppose yes, because it accesses InitialPoint and TerminalPoint properties and pass them as parameters to the MethodWithThreePoints
, which accesses X and Y properties of those points. In the other words, MethodWithThreePoints
uses properties of properties of objects passed to the class method.
If it violates the Law of Demeter, then I can't see best and reasonable solution for this problem. I know, I can add additional properties to the LineSegment class to satisfy LoD:
class LineSegment
{
...
public int InitialPointX
{
get { return InitialPoint.X; }
set { InitialPoint.X = value; }
}
//etc...
}
But when I want to invoke MethodWithThreePoints
within MethodWithPointAndLineSegment
it forces me to create new points: new Point(segment.InitialPointX, segment.InitialPointY)
... and pass those new points to the MethodWithThreePoints
. It introduces some additional and unwanted performance cost, because I have to create new objects and pass to them constructors values returned by the multi-level accessors.
I'm not sure, what would be the best solution for this problem and for many similar problems: satisfy LoD or convenience and in this case performance (those methods will be invoked many times in a short period for performing algorithms calculations).
Any suggestions and explanations are welcome.
I would agree to say LoD is, as any good practices, a guideline you must adapt to your context.
I will certainly not tell you to send it to hell though. It reveals problems in your design.
If you are familiar with OOP, you know you should design highly coherent class which combine data and behavior, and tell them what to do (see Martin Fowler: http://martinfowler.com/bliki/TellDontAsk.html)
I have not enough information here to help you much, but I can tell you your Point and LineSegment class are simple POCO (no behaviour, only public get/set). It is only data without behavior, which is not really OOP friendly. It explains why you are tempted to manipulate this data in a service (even if you call your service "Geometry").
I Guess in your code Point and LineSegment represent the data, and Geometry represent the behavior you'd like to have.
A more OO design, let's say to add a translation behavior could be something like:
class Point : ITranslate
{
public Point(int x, int y)
{
Y = y;
X = x;
}
public int X { get; private set; }
public int Y { get; private set; }
public Point Translate(Translation translation)
{
//return a new translated point
}
}
See how I keep my data for myself and just exposed the required behavior. Here I also design an immutable point, but we could also do something like this:
class Point : ITranslate
{
public Point(int x, int y)
{
Y = y;
X = x;
}
public int X { get; private set; }
public int Y { get; private set; }
public void Translate(Translation translation)
{
//apply the translation to myself internally on my X and Y
}
}
Of course I would need more context to be more helpful, but I hope it already answers your question.
Just to be clear, I don't say you absolutely have to do that here, I just explain why you find it hard to respect Demeter in your case.