Posted by Mark Withall: 2015-05-03
In object oriented programming the Law of Demeter states that a class shouldn’t reach through associated classes into the details of other classes beyond. This is often stated as “use only one dot”. This formulation, on the face of it, seems to have worrying implications for the use of fluent APIs, such as .NET LINQ.
Below is a simple example with two levels. The first represents a train and the second the details of each stop that the train makes. The Law of Demeter says that if we are playing with trains, we shouldn’t be able to see the details of stops, i.e. something that has a train, shouldn’t look at departure times. I
First Level Interface
public interface ITrain
{
int Id { get; }
IEnumerable<IStop> Stops { get; }
}
Second Level Interface
public interface IStop
{
string Name { get; }
TimeSpan? DepartureTime { get; }
TimeSpan? ArrivalTime { get; }
}
Here are two example methods that take a list of trains and use LINQ to filter them in some manner. Both return a list of stops but the first complies with the Law of Demeter and the second does not.
Legal Usage
public IEnumerable<IStop> Legal(IEnumerable<ITrain> trains)
{
return trains
.Where(train => train.Id < 100)
.OrderBy(train => train.Id);
.Select(train => train.Stops.First());
}
Whilst there are many dots used in the above code, nothing reaches through the ITrain
interface and into the details of the IStop
interface. Hence, the Law of Demeter is complied with.
Illegal Usage
public IEnumerable<IStop> Illegal(IEnumerable<ITrain> train)
{
return trains
.Select(train => train.Stops.First())
.OrderBy(stop => stop.DepartureTime)
}
Here there are in fact fewer dots used than in the legal example above. However, we are reaching through into the details of the IStop
interface (accessing the DepartureTime
property that the Law of Demeter says we shouldn’t know about).
As we have shown above, it is perfectly possible to comply with the Law of Demeter whilst using fluent APIs but it is necessary to be careful when doing so.
Tweet Follow @MarkWithall