0

I have just started oop and decided to program a checker's engine. I have few concerns about the way that I have structured my code and I am not sure if I am failing to understand the principals behind oop. My concern is about the handling of move generation, and board evaluation. At the moment, I have these two responsibilities separated into their own respective classes that other classes use.

I guess my question is should these responsibilities be in their own classes or do they need to be tied to a class like AI? But if I tied the responsibilities of move generation and board evaluation to a class like AI, does that break the SRP?

2 Answers 2

2

A (chess) Board may keep the positional data and accept moves that change it. It can handle complex moves like promoting a pawn to a queen, or castling. It can validate moves and reject invalid ones. It can check for conditions like attack, checkmate, etc. The board's responsibility is only the position, and how it stays correct.

A Piece may offer possible moves and attacks, given the position.

An AI may take a Board and make a move. So can do, say, some HumanPlayerInterface. This interface's only responsibility is making a single move. Definitely the AI thing will have a lot of other moving parts inside it; it does not change its single responsibility of making a move.

1
  • 1
    funny, I built a strategy game engine the last months, and that's exactly how I designed it. Glad to hear others think that this is a good way.
    – Aganju
    May 2, 2018 at 1:09
0

So, the thing about all these principles is that they aren't just there so that code would have certain aesthetic appeal - they have a practical purpose. In particular, they are concerned making code maintainable, and with making it possible to change, extend or replace parts of it without too much hassle. Now, the SRP is about having your classes "do one thing", because if they do more then "one thing", then the code doing these different things will most likely become coupled in subtle ways, and as a result become resilient to change - in the sense that it will be difficult to change these parts separately. Which means you can't really tell what the separate responsibilities are just by looking at what classes and behaviors you have to implement (at best, you can make some educated guesses, but this requires experience).

Instead, you have to try and understand what's most likely to change and in what ways - and you will most likely not get this right on the first try, even if you have experience. Your code is a software model (a representation) of this logical concept that is the system you are trying to build (the concept that includes all the real-world forces that may affect what that system is, and how it will change), and what OO design is trying to do is capture what the different parts of that system are, in what ways will the system change, and how to put these different components together to accommodate all that. And there are always multiple ways to model such a system; some ways will work better then others in practice. It's you (or your team) who has to define what "single thing" or "single responsibility" means for the system you are trying to build and maintain (as well as how much work is not "too much hassle").

That said, there are often things that change at the method level and class level as we develop the code, as we're trying to figure out how to implement some functionality. So it's good to apply SRP and other principles somewhat preemptively at this level to the subproblem we're solving, especially because at this scope we can better predict what we may want to separate, or where we may want to change the implementation later. Similarly when it comes to closely related classes. So, at that level, since these are more or less the kinds of programming problems we are all familiar with, we can tentatively talk about SRP being violated or not, but at the system level, it requires a deeper understanding of the requirements and the system itself, as modeled. At that level, it's also important not to overdesign the system up front.

But, back to your particular case; generally, it seems like a good idea to separate these out. Now, you may have another class (like your AI) that uses the others - its responsibility would be to coordinate what the other classes do. Just don't move the code from these other classes to the AI class - keep each peace of code in a separate place, and let each do its own work. It's a hierarchy of responsibilities, in some sense.

Finally, the fact that you are doing this for practice, for the purpose of trying to understand the OOP principles, means, on one hand, that you have some freedom to explore, but on the other, that you won't get change requests and bug reports from clients that will ultimately be the test of how appropriate your design choices were. So try to compensate for that in some way. Take your current design and study it. When you design your classes in a certain way, it's always a tradeoff of some sort: maintainability vs complexity, performance vs clarity, modularity in some ways vs rigid structure in others, etc. Try to understand which parts of your code have become easier to change, and which have become harder. Try to change the implementation to test if your understanding is correct. Then try to come up with a different design and study it. What changed? What did you gain, and what did you loose? This will help you to understand the reasoning behind SRP and other principles, which is what really matters.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.