Question

I'm working on a program that solves a certain type of systems of equations. The main data objects are Equation, Variable, Solution. Then I have this interface, which represents all things that I want to expose to the UI layer.

public interface ISystemSolver
{
    // Configuration
    int NumberBase { get; set; }
    bool CaseSensitive { get; set; }
    bool LeadingZeros { get; set; }

    // State
    IReadOnlyList<int> ForbiddenValues { get; }
    IReadOnlyList<char> ForbiddenVariables { get; }
    event EventHandler OnTooManyVariables;
    bool AreThereTooManyVariables { get; }

    // Variables
    IEnumerable<Variable> Variables { get; }
    void RemoveVariable(int index);
    void AddVariable(char variable, int value);

    // Equations
    IEnumerable<Equation> Equations { get; }
    void RemoveEquation(int index);
    void AddEquation(string equation);

    // Solving
    IEnumerable<Solution> Solve();
}

The class implementing it would certainly violate the single responsible principle. How do I avoid this?

I had this idea: Extract particular regions into interface. For example, the 3 methods that are dealing with variables would form an interface called IVariables.

But then, wouldn't it be a violation of the law of demeter when I need to for example add a variable? Because I would need to call SystemSolver.Variables.AddVariable(...)

Was it helpful?

Solution

First of all, thank you for thinking about your design and trying to follow some principles. I don't enjoy wading through code produced by those that don't.

Law of Demeter

Let me demystify the Law of Demeter for you. It's not a dot counting exercise.

SystemSolver.Variables.AddVariable(...);

Doing that is the same as doing this:

Variables vars = SystemSolver.Variables;
vars.AddVariable(...);

Either way, this can be good or bad depending on the relationships here. If SystemSolver and Variables are intimate friends that were intentionally and explicitly designed to be used this way, such as with a Domain Specific Language that is trying to let you construct something, then sure fine knock your self out. You're using this as it was intended and this can be very powerful and expressive.

But if you're just randomly grabbing stuff because you happen to be able to find it this way you're poring super glue all over the legos. That is, you're deciding that these separate classes must have an intimate and immovable relationship. Making that decision should not be the responsibility of clients using those classes. That should be up to the designer of those classes. If you do this, and someone decides to change the relationship, don't cry because your client broke. You were never promised that this would keep working.

I could go on but I've talked about Demeter before.

Dependency Injection

If you don't want to create such an intimate relationship how are you going to get what you need? You let it be handed to you.

SystemSolver ss = new SystemSolver();
Variables vars = new Variables();
UI ui = new UI(ss, vars);

There, now UI knows about what it needs but they don't even know about each other. Does that make sense? Maybe Variables are meaningless without SystemSolver making them. If so then maybe they do have an intimate relationship. If so then design them that way so dotting around to find what you need is not a violation of Demeter.

Single Responsibility Principle

When something has only one responsibility it should only have one reason to change. That doesn't mean when you look inside you only find one thing. It means everything you find inside is focused on the same thing.

Think about your car. Almost everything in your car is a car thing. The seats are car seats. The tires are car tires. The dash board is a car dash board. The coffee cup is a, wait whats that doing in here? While a lot of things can fit in a car some things aren't things you want to find in there when you buy the car.

Changing the cup shouldn't effect the car. Changing the car shouldn't effect the cup. At most changes to either should only effect the cup holder. That's why we really care about following SRP. It limits the scope of change. I've also talked about this before so I'll stop here. (Man have I been on this site for that long?)

Why You're Not Violating SRP

"[ISystemSolver] represents all things that I want to expose to the UI layer."

That is a single responsibility. It's a big one. But it's still single. You want to break it up, which is good, but SRP isn't why.

You have its methods broken into 5 different groups that are each about one idea. That to me signals 5 responsibilities. Is that bad? No. These responsibilities are all still focused on providing the UI what it needs.

But those 5 should be allowed to cluster together. This reminds me of

The Facade Pattern

Each of those 5 groups could be a separate class. Objects of which are added to the UI's facade class that delegates it's work to each of them. This allows things to separate but keeps the complexity of the structure hidden from the UI. It also would make the Law of Demeter something you no longer have to worry about because everything you need is right there.

Tell, don't ask

You are using a lot of getters and setters. Understand that these break encapsulation and lead to writing procedural code. It's much more powerful to tell objects what to do and let them decide how to do it rather then ask them something and make a decision based on what you learned. I wish I'd written this one but I'm glad I read it.

Licensed under: CC-BY-SA with attribution
scroll top