0

I am trying to write a blackjack simulation using OOP.

I have written it in a non OOP way before, but now that I found out JavaScript has private properties now, I try to get familiar with it and write the blackjack simulation using JavaScript.

When I get to the Player class, I had something like:

class Player {

playAGame() {

} }

However, I soon find out when writing playAGame() that I am dictating what the Deck object should do (the object for the deck of cards, so I will have player.takeACard(deck.dealACard())), or if the Deck object is inside of the Dealer class, then I am like dictating what the Dealer should do. So this doesn't look like what the real world situation is.

Alternatively, I can put playAGame() in the Dealer class, but that appears also to dictate what the Player object will do, which also is not the case in the real world.

So I can only think of:

  1. Using a Table class, which is like, something that governs all the rules happening at a blackjack table.

  2. Or perhaps a Game class, and act similar to the Table class: governs all the rules happening at a blackjack table.

  3. Or I can even use a World class (or a Universe class), that just let the player "sit down" and "want to start a game" and "want to get one more card", but the dealer can theoretically "disallow him to sit down" or "refuse to give him one more card" (not acting professionally), or the player can theoretically start a game and want to leave before the game is over.

So I am not exactly sure how to model it. It appears it maybe #1 or #2 above. Or it may be #3 above, but just let everything behave in a proper way (according to the standard rules).

How is it done if done in a proper OOP way?

ksl
  • 29

2 Answers2

3

OOP doesn't work like the examples

You probably won't have human style classes like Player, Dealer, Card, Table. You are more likely to have random classes that fit the way you program, DeckShuffler, Account, WinCalculator etc

You won't have Dog : Animal style inheritance, you'll have WinCalculatorXmas : IWinCalculator or Deck : List<Card>

Work out your algorithms and flow first, and then think about what classes come out of that, rather than trying to describe the game in human terms and making classes that match.

For turn based games like blackjack you are probably going to end up with some kind of GameState class and multiple Rule Evaluators which you apply in a state engine.

Ewan
  • 83,178
3

There is no one true proper OOP design. So you'll still have to make some arbitrary decisions. But there are principles and concepts to follow if you want to stick to OOP.

I have written it in a non OOP way before

Yeah? What way was that? There are many non OOP ways. What were you doing?

but now that I found out JavaScript has private properties

That exists in service to one of the concepts of OOP: encapsulation. If you're thinking you can only follow this because JavaScript now has private properties you've never done OOP in Python. It's access modifier is simple. Put a leading underscore in the name and everyone will know you consider that property private. Rather than language support they have a convention. The underscore says any code that decides to depend on this property does so at it's own risk because you reserve the right to change what it does or if it even exists. Designing what you make publicly available is hard. Encapsulation lets you hide some stuff. That way it doesn't have to be designed robust enough to support every use the rest of the system might later demand of it.

How is it done if done in a proper OOP way?

Any of your three ideas could be done in a "proper OOP way". OOP doesn't dictate a particular design. It does advocate some habits that would make your code recognizable to other OOP practitioners.

However, I soon find out when writing Player.playAGame() that I am dictating what the Deck object should do

That's a hallmark of procedural programming. When contrasted with OOP a procedural programmer tends to pull details towards them and nail them all down in one place. A OOP programmer will push those details away and hide them behind abstractions so they don't have to think about them. I prefer this because it tells the story simply at a single level of abstraction while pointing me to where those details are hiding if I happen to care about them.

In OOP rather then the Player dictating what the Deck does, the Player sends a message. The Deck decides what, if anything, to do about that message. What the Deck does is its business.

These messages can be done with events, method calls, or carrier pigeons. The important thing is the Player doesn't get to control or know what the Deck will do. That knowledge should be carefully separated from the Player. To prevent that knowledge the Player should not know which implementation of Deck it's talking to. That gives us another OOP concept: polymorphism.

Something, somewhere, has to know which implementation of Deck you're using. Construction code solves that problem. One thing to understand about OOP is in most languages it doesn't stretch to cover all your code. Construction code is typically done procedurally (see dependency injection). But after an object graph is constructed and woken up by calling some start method on it we're in OOP land.

Smalltalk only avoided making you write such code by making you construct objects with your mouse in a menu system. In most languages you don't have the ability to keep your code that pure so you have to mix in other kinds of programming. While construction code is done procedurally, OOP should be used to solve the behavior problems. That gives your code a dry boring crusty procedural outer layer while the juicy interesting inner core is OOP.

if the Deck object is inside of the Dealer class, then I am like dictating what the Dealer should do.

Well if the Dealer is composed (another OOP concept) of a Deck then the Dealer can use that Deck to control it's own behavior. It's hard to be a dealer without something to deal. We have discovered that such "has-a" relationships can be a very flexible and powerful way to share behavior. See the adage composition over inheritance.

Another OOP idea is "tell, don't ask" which asks you to respect encapsulation by not asking objects to expose their internal state by asking them questions about it. Rather, tell the object that knows the details of what needs doing and let it do whatever it's going to do. If you need to know what happened let something else tell you. If you stick to just telling it what you want at the right time and not worry over what was done the code stays nicely decoupled.

Also, don't think only real world things can be objects. Say we wanted to count the cards like a card sharp does. It's perfectly ok to have a CardCount class that takes all the responsibilities of knowing how many points a face card has and such. Each CardCount object can worry about adding up what ever cards it's seen dealt. And any player whose seen those same cards could know that count.

Hopefully with this you can see how the OOP paradigm shift is far more about how you think about coding then any language feature or particular design. There are many ways to solve the problem. I find I'm being most faithful to OOP when I'm looking at the problem from the perspective of an individual object. What does it care about? What doesn't it care about? What part of this can it make something else do for it?

candied_orange
  • 119,268