-1

Taking into account the accepted answer here, the idea for general recommendations is that, where possible, favor defining classes over interfaces.

Consider the following design:

  1. A base class ChessPiece, which multiple child classes are also defined
  2. Some types of piece needs a record if they were already moved (namely Pawn, Rook, King): a bool HasMoved property
  3. A type of piece, that being the King, cannot be captured; everything else can be: a bool IsCaptured property

The problem is the conflict between #2 and 3 (there's no problem if I implement only one of the two) because of the derived class King, which is common in #2 and 3.

Ideally, both abstract classes CapturablePiece and MoveRecordedPiece, which both inherits from ChessPiece, should be implemented. But C# doesn't allow for multiple class inheritance, and that's where interfaces come in.

If interfaces were implemented, now all concrete child classes can manually implement each required property. Consider then this example:

// needed type-check
ChessPiece piece;
// some calculations...
if (piece is ICapturablePiece)    // or using the 'as' keyword
//or...
if (piece is IMoveRecordedPiece)
// which in the case of non-conflicting abstract classes,
// this is easily doable and valid because of
// inheritance to the base class

I cannot do this since interfaces are as-is. It cannot inherit from anything, it is just a standalone marker for methods that must be implemented.

The abstract class versions are still preferable - a CapturablePiece and a MoveRecordedPiece, since its base type is ChessPiece, and I'm sure this logic is an "is-an" instead of a "can-do" (as mentioned in the link above).

My question now would be, how would I improve the design considering:

  1. The fact that you cannot inherit multiple classes, and interfaces cannot inherit anything.
  2. The favor of abstract classes over interfaces
  3. The code block where type-check is needed

EDIT:

This is not a duplicate of the linked question. This is more of a how to improve design while considering interfaces and abstract classes.

  • I guess it was mostly answerd [here](https://stackoverflow.com/questions/747517/interfaces-vs-abstract-classes) – Malior Apr 02 '19 at 08:38
  • Bear in mind that the "prefer classes to interfaces" in the Microsoft Design Guidelines comes from the problem of backwards compatibility - you can't add a method to an interface without breaking backwards compat. If these types are all just used inside your own code, that is not a concern, so don't worry about it. In your example, I'd use interfaces - the interface describes some behaviour of the object - "this object can be captured", "this object can keep a history of its moves", etc. Although in practice, I wouldn't use any interfaces here: `ChessPiece` would have `bool IsCaptured` – canton7 Apr 02 '19 at 08:40
  • Reading your question again, can you explain what you mean by _"I cannot do this since interfaces are as-is"_? – Llama Apr 02 '19 at 08:43
  • Possible duplicate of [Interface vs Abstract Class (general OO)](https://stackoverflow.com/questions/761194/interface-vs-abstract-class-general-oo) – Circle Hsiao Apr 02 '19 at 08:45
  • @canton7 sadly this cannot be the design since in the game Chess, based on also provided info, there **is** a type of piece that cannot be captured. – Jennifer R. Mullins Apr 02 '19 at 08:45
  • @JenniferR.Mullins so? Then that property will never be set to `true` for the King. It's a lot easier and quicker for your game logic to check `piece.IsCaptured` than `piece is ICapturablePiece capturablePiece && capturablePiece.IsCaptured`. Be pragmatic here. – canton7 Apr 02 '19 at 08:46
  • @John thank you for mentioning that storing an `IsCaptured` property is not needed. I'll definitely consider this thanks! – Jennifer R. Mullins Apr 02 '19 at 08:47
  • @John can you paste back your first comment? For reference only thanks. Anyways, what I meant in there was I cannot implement any definition, just the signature, and also cannot inherit from other classes, if interfaces were to be used. – Jennifer R. Mullins Apr 02 '19 at 08:50
  • 1
    [This series](https://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/) by Eric Lippert is required reading for people trying to combine type systems with game rules. Make sure you read all 5 parts - part 5 in particular is the important one. – canton7 Apr 02 '19 at 08:52
  • @canton7 thanks for this info, in fact I already started it, I just paused at part 2 or 3. Will resume reading thanks. – Jennifer R. Mullins Apr 02 '19 at 09:00
  • Sorry, my comment was essentially: you don't need captured, since you should move captured pieces out of the set to consider (it will be good for performance if nothing else). As for the "moved" flag, you might as well maintain it for all of them considering how little data it takes up. – Llama Apr 02 '19 at 09:03
  • 1
    Thanks again @John. – Jennifer R. Mullins Apr 02 '19 at 09:08
  • @JenniferR.Mullins Part 5 is the one that says "don't put your game rules in your type system" - which is what you're trying to do in your question. – canton7 Apr 02 '19 at 09:10
  • This looks very opinion based, there are good reasons for any design choice and it depends on what you would use them for, as is, I think your question would only attract opinion based answers, as I don't really see a problem represented that is blocking you. Maybe you should look at software engineering or if you want your code validated, look at codereview instead? – Icepickle Apr 02 '19 at 09:11
  • @Icepickle I see, thank you very much for pointing out codereview. – Jennifer R. Mullins Apr 02 '19 at 09:14
  • What happens when a Pawn attacks the Bishop with a Rock on a full moon? Eric Lippert has got that covered in way more details than any of us can in an SO post. You should go back to reading his blog series. Once you've completed chapter 5, if you still have questions, that would be a good time to post them here. – Zohar Peled Apr 02 '19 at 13:17

1 Answers1

0

Define behaviour independently from each other and aggregate it via the requiring instances. Your problem is not inheritance, but too much responsibility in one class. A King moves differently from a Pawn. So define the behaviour of these types independently and add it to the chess figure class. Similar to the movement. Implement that behaviour externally and pass it to the classes which require it.

This let's you decouple your code nicely.

Florian Salihovic
  • 3,760
  • 2
  • 15
  • 23
  • My problem is **not** about each piece's moves being different and they're sorted all right (Pawn's move implementation is different from King's, and so on). It's about implementing the problem stated above. – Jennifer R. Mullins Apr 02 '19 at 09:06