-1

So now i have a really concrete example; its highly related to that question here: Tell one, but ask the others?

the important statements there are:


In the comment section https://softwareengineering.stackexchange.com/a/418453/347781 :

But what you would say if both sides pull out the data?, ok i put the behaviour to the Chatroom, but the Chatroom pulls then out the data from the user, because it needs to fullfill its algorithm data from both,....... maybe then we have a third party, fourth and so on, The Chatroom would pull out data from all of the others. What kind of benefit i have to put then the behaviour to the Chatroom. On which metric i can decide to put it to the Chatroom, because you said placing the bevahiour to that location, which dont pull out data, but if each one of them pull out data?^^ – Robin Kreuzer

@RobinKreuzer You have to come up with a design in which no objects pull data out of other objects (some rare exceptions apply). That is what object-orientation is at its core. Objects are there to contribute behavior. It is the key factor that makes oo more maintainable than procedural programming. – Robert Bräutigam

and my abstract example here:

if(
something from user &&
something from ChatRoom &&
something from something deep in the user, maybe the color of the users fingernail? &&
something merged from user and Chatroom (the niceLevels for example) &&
something from clock &&
something ....
)

then

{
change some state in the user
change some state in the ChatRoom
change some state in the niceLevels of user and ChatRoom
change maybe the clock?
change something in a third-party-object, which was not in the if-checks (maybe sending a notification/email or something else)
change some state in ....
}

the quote and the abstract code example are both from the Tell one, but ask the others topic the abstract example should show how complex interacting/collaborations of objects could be; that it is not that easy like Robert Bräutigam claim in his quote.


Now i want to give a concrete example/question:

Let's take a chess game, if a client/user wants to move the tower five spaces forward, then there must be a few constraints full-filled.

  • The tower is not allowed to cross an own figure or a other figure (a horse is allowed to do so)
  • The tower can throw hostile a figure from the game if it is on field five spaces forward from him.
  • And so on

Now i want to add some game-rules:

  • If the a hostile figure is threw away, all fields the tower touched are colored red, if no figure is threw from the game, all fields the tower touched are colored grey. The horse color its field in all cases green.
  • If a tower and only the figure tower reaches the other side, the game-mode is changed, so all figure should now act in a other way. Maybe game-change could be that no more figures are allowed to be thrown from game the next 10 turns, and all figures are now only allowed to move in maximum 2 spaces
  • if a farmer reaches the other side, it is morphed to a queen and all towers a player left should morphe to horses
  • All figures can be named by the user and its movement action are counted, if a figure is morphed it keep that informations
  • a runner is only allowed to throw a hostile figure from the game, if itself moved at least 15 times and only 17 figures (sum of all figures from both teams) are left

And now we have there still a few more (more technically) requirements:

  • The client is only then allowed to move one of its figures, if when it is his turn^^
  • the game is controlled cli-based/text-based, so the user sends text-command to select one figure and signalize its movement (how you would dispatch this?)

So i think i have now a great NOT abstract examples, what was wished by so many of you. Im interested now, how you would model this to full fill Robert Bräutigams constraints, which is more or less the same as Demeter had too.

A few thoughts from me

  • Maybe we can add the whole behaviour into the figures?, but then it have to pull out the state of the world around it?! -> a more or less procedural approach, because one code-actor/procedur acting on the other state?
  • Maybe we should try to divide the behavior into the figures and the world around, but i can't see that the get there the wanted transactional-style. Also we get into trouble if we want to change the game-mode, we have to change it in all objects? And i think its not possible alone doing it that way, because we need merged-state ("runner is only allowed to throw [...] are left") -> the hoped OO-approach from Robert Bräutigam?

.

Ok now i stop here with my thoughts^^, i don't want to suggest that there is no solution on that i am interested in, that hopefully exists

Edit:

ok i thought it it was clear what the focus of my question is, because it linked and related that question here to the abstract one Tell one, but ask the others?

Anyway, my question is how you would model my real-world Robins magic chess game in that way that it full fills the constraint given by Robert Bräutigam (see the quote at the beginning) being then a object oriented model that doesn't pull out data from others and what the benefits are doing it like that way.

I hope the focus is clear now.

So i also don't look at a generic chess-game model-solution (as Doc Brown thought, because he linked to a search of chess-game solution, but on a special kind of modeling, like i described beginning at "Anyway, my ques....")

2 Answers2

3

A game of chess is fully described by the list of moves that have taken place and the rules of the game. (8x8 board, starting positions etc)

If you want to program it in a OOP style your objects are not Piece, Board etc they are things like Turn, Move, Game, NotiationParser, BoardDrawer and the like

When learning OOP all the examples focus on real world objects, "a Car has four Wheels", "a Cat is an Animal" etc but in actual programming your objects are abstract collections of data and functions that operate on that data.

With real world objects we can see real connections between them, we know a Car is not an Animal. But with games or business rules there are no outside constraints.

We say "a Pawn is a Piece" because we are used to the physical game pieces, but in terms of the game rules it's completely different from the other pieces. It has crazy rules which depend on the position of other pieces and their move history! It can change into a different piece!, It knows about the board orientation!

struct Move
{
   position From
   position To
}

class Rule { public Rule(list<move> movesSoFar) {...} } class PawnMoving : Rule { bool IsValidMove(Move m); { //is there a pawn on the from square? //is is there a piece on the to square? ...etc } } class WhosTurnIsIt : Rule { bool IsValidMove(Move m) { //what colour piece is on the From square //what colour moved last? //are we castling? } } class CanOpponentTakeKingAfterThisMove: Rule { bool IsValidMove(Move m) { ... } }

Ewan
  • 83,178
2

Of course you can have the pieces holding the necessary logic. The only problem you have, as you mentioned, is that you need all the information about the board and positions too.

There's a couple of possible options how to deal with that:

  1. You can pass all that information into the piece when necessary. For example on each invocation. of move or similar.
  2. You can pass updates to all pieces and let the pieces manage their view of the current board as they see fit. This enables some optimizations for specific movements for example.
  3. You can invert the above, and let the "board" manage most of this, and have the pieces manage only movement patterns. This is usable if the most of the rules apply to most of the pieces. I'm not sure they do, you have some strange rules there.
  4. You could control pieces or let them control themselves. For example you could either tell them to "morph" or tell them what happened and let them morph if they want to. There's a trade-off, but both are "oo compatible".

What I'm trying to describe is that even if you need to have lots of data at some point in the code to make a decision, you can manage that without pulling it from others. An alternative is to just pass it in, or let the object manage it, or even duplicate it. As I've said before it all depends on the exact requirements.

Also, here is an OO Tic-Tac-Toe game that has no getters at all, that demonstrates how to deal with some of the same problems you mentioned. This game was designed to be extensible with GUI and network players without changes to existing code.

Update: There were a lot of questions, so let's dive in:

  1. What would I pass in? Certainly not a Board object. Since the Piece would like data for its function and not an object.

Here, again, there is a distinction between data structures and objects. It is always tempting to just "group" certain data elements into a structure and pass the structure. You have to resist this urge.

While it may simplify some signatures in the short-term, you now create a structure which has no abstraction capabilities and likely tied to the consume or producer or both. It can never change independently. Which is bad.

So I would pass in exactly the data the Piece needs, a list of pieces or even an array of pieces, or an array of "cells" with the pieces in it. The Piece defines the contract (by having a public method), and others can choose to call it.

One other benefit of passing "data" along instead of pulling it out, in addition to the contract being defined by the consumer, is that the object managing that data can still retain control. It can decide how/whether/what to pass along.

  1. Have a look at the Tic-Tac-Toe example, this is actually done there.

  2. The Board does not have to micro-manage the Pieces. Have a look at the Game and Board classes in the Tic-Tac-Toe example. They don't manage the cells.

  3. A method morphToKnigth() is not a setter. Although there is a gray area at some point, where it is difficult to say. In this case it feels strange because this is a made up rule and has no "business"-related name.

"Morphing" a pawn to a queen is called promotion, so that would be a good, business-related method name that is clearly not a setter.

  1. What I mean by "duplicate" is just that. Pieces could hold onto their view of the current state on the board. This would enable pieces to hold highly optimized representations for example. Bitmasks of fields, whatever.

Object-orientation does not have the need to "normalize" data in the database sense. The same piece of data can be present in multiple representations, with differing or even same semantics.

I would have no problems (barring requirements to the contrary) having the state of the board held redundantly in all the pieces, if it would help/simplify/optimize my design in some way.