Chapter 7

Illustrating Object-Oriented iOS App Design

In This Chapter

arrow Creating a simple software development process for your project

arrow Identifying use cases and scenarios and creating an object-oriented design for an app

arrow Transferring a design to and implementing it on the iOS framework

Prior chapters in this book focus mostly on explaining OO design, except for a little hands-on work in Chapters 3, 5, and 6 (on Objective-C, Xcode, and an introduction to the iOS Framework, respectively). In this chapter you will see how to take the idea behind your app and design and implement it using OO techniques. That is, you find out how to design an iOS application from scratch.

Customizing an SDLC for iOS App Development

Before starting on the development of your app, I strongly recommend that you think about and design your development process (also known as your software development lifecycle, or SDLC). In other words, you need to decide how you'll develop the app (rather than what you will develop). First, you must decide how predictive and structured or how flexible and agile the SDLC will be. As I explain in Chapter 2, this choice depends upon the following:

  • How critical is my app's mission?
  • How likely is it that the app will change?
  • How skilled is the team (for current purposes, the team consists of you and me)?
  • How big is the system?
  • How comfortable is the team, (once again, you and me) with change?

To answer these questions, you must first understand what you're building (you’re building a Tic-Tac-Toe game), why you're building it (to learn how to build apps using iOS), and with whom you're building it (you’re building it with me). Here are some responses to the preceding questions:

  • Mission critical: The app isn't mission critical; no one is likely to be injured playing Tic-Tac-Toe, which means you could use an agile process.
  • Likelihood of change: How likely are the requirements of the app to change? We both know what Tic-Tac-Toe is, so the game part of the app is pretty well understood. This means you could use a predictable, structured process.
  • Team's skill: With you on board, the team is pretty skilled. Given that we’re both pretty skilled, I think we could work together in a very agile manner, although if we went about app development in a well-structured manner, it wouldn’t do any harm.
  • Size of system: The system is small. In fact, you might be hard-pressed to call the app a system. A small system could easily accommodate an agile process.
  • Team's comfort with change: You are, after all, reading this book to find out how to develop iOS apps, and you probably wouldn’t like it if I made all kinds of changes to the app as we go along. So, you probably want me to tell you up front what I'm going to build and to stick with the plan. I’d say this calls for a structured process.

Some of these responses suggest an agile process. However, most of them support use of a systematic, structured process. So that’s the process you'll use in this chapter to understand iOS app development — using Tic-Tac-Toe as the example. Specifically, you'll do the following:

  • Develop use cases for the Tic-Tac-Toe app.
  • Design the user interface using pen-and-pencil sketches.
  • Produce an OO design of the app and its logic.

You begin with an OO design without considering the implementation platform — iOS. Doing so makes the design process easier and results in a good design from an OO perspective.

Once you have the initial design, you map the design onto the iOS platform. Finally, you implement the design — that is, you write the code for the app.

tip.eps Think of this process as being one for a fancy dessert recipe that you must translate according to what’s in your kitchen.

Developing Use Cases

Your first step is to identify what the app must do. In techie-speak this is known as capturing the app’s functional requirements. Functional requirements are typically captured as use cases. A use case is a description of a specific interaction between an actor (an entity external to the system, such as a user or another system) and the system being designed.

Developing use cases begins with creating a written description of the app. Here is a simple description of Tic-Tac-Toe:

Tic-tac-toe, also spelled tick-tack-toe, or noughts and crosses, as it's known in Britain and some of the Commonwealth countries, is a pencil-and-paper game for two players. These two players take turns marking the empty spaces in a three-by-three grid, one player using the symbols X and the other player using the symbol O. The X player usually goes first. The player who succeeds in placing three of his marks to fill a horizontal, vertical, or diagonal row wins the game.

Now, here's a narrative that extends the preceding description about playing Tic-Tac-Toe on an iOS device.

Tic-Tac-Toe for iOS implements the Tic-Tac-Toe paper game as an iOS app. Users can play the game against the computer. Multiple games can be played in each session, with either the computer playing first or the user playing first on an electronic board shown on the device’s touchscreen. Scores for each session are accumulated. If the user quits the session, scores are reset.

The next step in developing use cases is to identify the actors and write down their interactions with the app. In Tic-Tac-Toe, there are either two actors who are the players of the game or one human actor who is playing against a computer (which is considered a system actor). The interactions of the actors with the game are as follows:

  1. A player starts a game session.
  2. A player makes a move (that is, places an X or an O in any empty square).
  3. The game ends in either a win or a draw. When the game ends, the players are notified of the result.
  4. A player continues a game session and starts another game.
  5. A player terminates the session, which could happen in the middle of the game or after a game is over.

Here's the general format of a use case (once again, for a specific interaction with the system):

  • Title of use case
  • Actor or set of actors interacting with the system
  • Starting point of the interaction, also known as the assumptions necessary for the interaction
  • Short description of the interaction
  • Outcomes of the interaction

Consequently, the use cases for Tic-Tac-Toe are as follows:

  • Use Case 1: Player starts a game session.

    Actor: Player.

    Assumptions: The app is running. One of the players is presented with a user interface option to start a new session.

    Description: This use case describes the interactions that take place when a player creates a new Tic-Tac-Toe session. Starting a session automatically starts a new game.

    Outcomes: Scores are set to 0 for both players. A new game starts, and the players see a blank three by three grid to play on.

  • Use Case 2: Player makes a move.

    Actor: Player (could be Player 1 or Player 2).

    Assumptions: A game session and a game are active (see Use Case 1).

    Description: Player 1 makes the first move. His move consists of placing an X in an empty square on the game grid. Player 2 goes next by placing an O in an empty square. The players then alternate until the game ends.

    Outcomes: After each move, an X or an O appears in the appropriate square on the grid.

  • Use Case 3: The game ends, and the players are notified.

    Actor: Player (could be Player 1 or Player 2).

    Assumptions: A game session and a game are active. A player (Player 1 or Player 2) just made a move (see Use Case 2).

    Description: If the move results in every cell in a row, column, or diagonal being filled with the same symbol (either an X or an O), the game ends. The last player to make a move wins, and his tally is incremented accordingly. If the move doesn't result in a win but all the cells are filled leaving the next player with no place to play, the game ends in a draw.

    Outcomes: The players are notified of the outcome and the updated scores and are asked whether they want to continue the session by playing another game.

  • Use Case 4: Player continues a session.

    Actor: Player (could be Player 1 or Player 2).

    Assumptions: A game session is active. A game has just ended (see Use Case 3), and the players are asked whether they want to continue the session by playing another game.

    Description: A player elects to continue the session by starting a new game.

    Outcomes: A new game begins within the same session.

  • Use Case 5: Player ends a session.

    Actor: Player (could be Player 1 or Player 2).

    Assumptions: A game session is active. A game has just ended (see Use Case 3), and the players are asked whether they want to continue the session by playing another game.

    Description: A player elects not to play another game, so the session terminates.

    Outcomes: The session terminates. Scores are reset. The user is presented with an option to start a new session (see Use Case 1).

I used a simple notation to demonstrate use cases, but there are many formal formats for documenting use cases. You can find one to suit your taste by searching the web. Just enter the keywords Use case template in your favorite search engine. You can also find some resources in Chapter 15.

Creating the User Interface

Now that the use cases have been defined, you're ready to start sketching the user interface for the app. You can use drawing software, if you really want to, but I've found that using pencil and paper is just fine. You can even use a whiteboard or blackboard and then take a picture of the board.

Figure 7-1 shows a sketch of the app's startup screen welcoming you to Tic-Tac-Toe and inviting you to start a new game.

9781118799277-fg0701.tif

Figure 7-1: UI sketch for starting a new Tic-Tac-Toe session.

Figure 7-2 shows the user interface when the game is underway. Note that both players have made a few moves and it's Player 1's turn to play.

9781118799277-fg0702.tif

Figure 7-2: An active Tic-Tac-Toe game.

Figure 7-3 shows what happens when a game ends and the players are asked whether they want to play a new game in the same session.

9781118799277-fg0703.tif

Figure 7-3: Game ends! Play a new game?

The screen flow for the game is shown in Figure 7-4 (which is built from Figures 7-1, 7-2, and 7-3). Note that the screen flow is from Figure 7-1 to Figure 7-2 and then to Figure 7-3.

tip.eps I recommend developing the use case and the user interface (UI) together. You may want to follow this process, as I do at times:

  • Write down the concept for your app. Then start by sketching a few screens or writing a use case or two or, if necessary, by going back and refining the concept for the app. Basically, if you get stuck while doing one thing, switch to doing another.
  • Be prepared to iterate. Even at this high-level point in your, the process is by no means linear. Design involves lots of back-and-forth activity and erasure of previous work, along with wads of balled-up paper and pictures of whiteboards in the wastebasket. Keep in mind that in OO design and development, the end result is clean, but the process is sometimes messy.
    9781118799277-fg0704.tif

    Figure 7-4: Screen flow diagram of the Tic-Tac-Toe game.

Illustrating Object-Oriented Design

Your next step in designing your app in an object-oriented way is to extract software elements from the text descriptions of the app. That is, you need to extract objects, classes, responsibilities of these classes, and collaborations among these classes, from the app's description and use cases. In the following three sections, I show you how to extract these elements from the description of Tic-Tac-Toe, along with the use cases and sketches from the previous section.

These extracted classes, responsibilities, and collaborations make up your initial design of the app. Once you have this initial design, you can start translating it into code, as I describe in the section, “Implementing an Object-Oriented Design on iOS,” later in this chapter.

Classes and responsibilities

Start by extracting nouns from the description of the app and its use cases. These become potential objects, classes, and attributes of your app. Next, you extract verbs from the description and use cases. These become candidate responsibilities (potential methods of classes).

The following list shows how I identified, defined, and extracted nouns for the Tic-Tac-Toe app; then I do the same for verbs.

  • Nouns: Nouns that you may find in the descriptions and use cases of the Tic-Tac-Toe app are pencil, paper, game, nought, cross, player, X, O, space, symbol, grid, mark, vertical row, horizontal row, diagonal row, human user, human, computer, session, board, touchscreen, and score.

    Next, write down a one-to-two line definition of each noun in the context of the app that you’re trying to build. Then compare these definitions. If you find that two nouns are defined in the same way, remove one of them. You might also decide to merge two definitions (and therefore the corresponding nouns) into one. When you complete this process of definition, removal, and merging, you're left with a set of nouns that will serve as your candidate classes. Following is an example of this process from Tic-Tac-Toe (using a subset of the nouns and verbs, to avoid taxing your patience):

    • Remove the nouns pencil and paper as physical things not relevant to an iOS-based game.
    • Observe that symbol and mark mean the same in the context of Tic-Tac-Toe, so delete mark and retain symbol.
    • Observe that nought and O mean the same thing in the context of a Tic-Tac-Toe game and that cross and X mean the same, as well. So, remove the ill-favored British terms nought and cross, and leave O and X. Also, be aware that O and X appear to be either instances or subclasses of symbol.
    • Compare user and player. Retain player as the player in the game. Depending on the context, human user and human can be the same. These nouns, along with computer, are instances or subclasses of player.
    • Board and grid are similar enough in meaning that one of them can be removed. For current purposes, I kept grid and, in fact, gave it a new name: game grid.
    • What about touchscreen? It refers to a physical component of the phone, so you might be inclined to remove it. On the other hand, something needs to handle the visual display of the board. It could be the board itself. Or you could separate the data structure that represents the board from its visual manifestation, and that's what I did. But I renamed it board to help address the first issue that arises with it.
    • Consider row as a component of game grid and vertical row, diagonal row, and horizontal row as being different subclasses or instances of row (but you don’t know which yet).
    • Retain game, for obvious reasons.
    • Consider session as the manager of games, with score being an attribute of the session for the two players.
  • Verbs: Candidates for verbs in the Tic-Tac-Toe app are take turn, mark, goes, place, win, implement, play, playing first, display, accumulate, quit, reset.
    • Remove take turn and goes as being close enough to play, which you retain. For now, retain playing first and the missing playing second as potential refinements of play. The final design will ultimately show you that these last two verbs aren't needed.
    • When used as a verb in the context of Tic-Tac-Toe, mark can be seen as similar to play. That is, when you play by making your move, you're marking a location on the grid. So, remove mark and retain place, but rename it place symbol.
    • Remove implement because it isn't a responsibility relevant to the game; instead, it's relevant to the process of building the game.
    • Retain display, accumulate, exit, and reset as valid responsibilities.

You now have the following potential classes, instances, and responsibilities:

  • Classes: Symbol, Player, Human, Computer, Board, Row, Game Session, and Game (with the attribute Score).
  • Instances: O, X of the class Symbol.
  • Responsibilities (which will become methods): play, place, display, accumulate (scores), quit, and reset.

Now it's time to tentatively assign responsibilities to classes as logically as possible:

  • Allocate the Game Session class the responsibilities, play new game, accumulateScores, quit, and reset.
  • Allocate the Game class the responsibilities, play.
  • Class Board has Display responsibilities.
  • Class Game Grid has Place.
  • Symbol, Player, Human, Computer, and Row have no responsibilities. But don't delete them just yet. You find out what needs to be done with them in the next section.

Collaborators and missing classes and responsibilities

It's time to take a walk through several use cases to ensure that each use case is supported by a class and its methods (more accurately, an instance of a class and its methods). As part of this process, you also find out about the collaborations between classes. Because the Tic-Tac-Toe app isn't complicated, this exercise is simple and somewhat generalized.

For Tic-Tac-Toe, you can simply create a high-level scenario by combining multiple use cases; however, when you build a complex app, be sure to have detailed scenarios, each exploring a new set of interactions with the app. Here’s your generalized scenario:

  1. A player starts a new game session.
  2. The first player places his, or its, symbol (the X) in an empty location on the board.
  3. The second player does likewise. That is, he places his symbol (the O) in an empty location on the board.
  4. Repeat Steps 1 and 2 until either
    • One player has three of his (or its) symbols in a row, column, or diagonal. This could happen at the end of Step 1 or at the end of Step 2.
    • There are no more locations to play, so the game ends in a draw.
  5. Accumulate the scores for the players.
    • Both players’ scores stay the same when the game ends in a draw.
    • The number 1 is added to the winning player's score.
    • No change is made to a losing player’s score.
  6. If the player wishes, both players return to Step 1 to start a new game; otherwise, they quit.

Now go through each step to see which class handles it.

Ouch. You run into trouble in the first step! There's no class to support the responsibility of starting a new game session. It looks like this responsibility can belong in the Game, and certainly not in the Board class, so you need to create a new Game Manager class to create a new game session.

Game Manager and Game Session are therefore collaborators — because Game Manager creates an instance of Game Session.

In the following, Steps 2 and 3 describe what happens when a symbol is placed on a board:

  1. A symbol must be placed at a location and be visible on the Board. Creation of the Symbol itself is covered by Symbol. Board can cover placement of the symbol, if you add a place Symbol method to it. Location is a noun that wasn’t considered earlier. You evaluate whether it's a candidate class, but decide that it's an attribute of the Board, and not a class by itself.
  2. Board then must signify to Game that a play has been made.
  3. When Play is invoked in the Game, the Game Grid is updated (so it needs the responsibility of setting a symbol at a location. Call this responsibility setSymbolAtLocation.

    You now have to evaluate whether the game ended with a win or a draw. This looks like a Game responsibility and that Game will collaborate with Game Grid to see whether a row, column, or diagonal is complete. Game needs a responsibility, so checkResult and Game Grid will have the responsibilities isRowFilled, isColumn Filled, isLeftToRightDiagonalFilled, and isRightToLeftDiagonalFilled. Also, note that the classes Board, GameGrid, and Game are now collaborators.

  4. Finally, you have to switch to player 2. Let Game handle this logic.
  5. Rinse. Repeat the preceding steps.

At first blush, it looks as though the Game class should be responsible for accumulating scores, but it represents only a single game, and scores are accumulated across games in a session. So Game isn’t suitable, so you try Game Session:

  • Game Session now creates a new game, decides on and sets the players, plays the game, and accumulates the scores.
  • Game Session can also handle playing a new game or quitting the game, which adds a nice symmetry.

The updated scores must be shown somewhere. What about showing them using the Board class? That doesn’t work because Board shows the Tic-Tac-Toe playing surface — that is, the board. However, you can create a Game View class and add a Show Scores method to it. Game Session will use Game View to show updated scores.

Moreover, Game View can manage the entire visual aspect of the game. So, let it display the score and the players' alternating access to the Board.

Now you have the following classes, responsibilities, and collaborators:

  • Game: Represents a single Tic-Tac-Toe game.
    • Responsibilities: play, checkResult
    • Collaborators: GameSession, GameView, Grid.
  • Board: Represents the Tic-Tac-Toe board.
  • Game View: Represents the visual display of a Tic-Tac-Toe game.
    • Responsibilities: placeSymbol, showScores
    • Collaborators: Game
  • Game Grid: Represents the three-by-three Tic-Tac-Toe grid.
    • Responsibilities: setSymbolAtLocation, getSymbolAtLocation, isRowFilled, isColumnFilled, isLeftToRightDiagonalFilled, isRightToLeftDiagonalFilled, getEmptySquares
    • Collaborators: Game
  • Game Session: Represents a Tic-Tac-Toe playing session consisting of multiple games.
    • Responsibilities: playNewGame, quit, decidePlayers, accumulateScores
    • Collaborators: Game, Game View
  • Symbol: Represents a Tic-Tac-Toe symbol either an X or an O.
    • Responsibilities: None
    • Collaborators: Game

technicalstuff.eps Notice that the names of the responsibilities are similar to the names of the methods, which follows standard Objective-C naming conventions.

Finally, for each class, run through this checklist for a proper class:

  • Does the class have a suitable name?
  • Does it have a cohesive description?
  • Does it have responsibilities?
  • Does it have collaborators?
  • Does it and its components maintain state?

All the Tic-Tac-Toe classes just identified meet these criteria.

Contracts and signatures

You're making good progress. You have classes and methods, and you know which classes collaborate in the game. The next step is to clearly specify (or at least understand) what each method is supposed to do, and what it needs in order to do so. In this section, you go class by class, method by method, starting with Game (although the order in which you address each class doesn't matter).

  • The first method in Game is play. This method is called when an X or an O is played on a location. The location is identified by its coordinates. This method needs a Game Grid, a Symbol, and a coordinate (that is, x, y) position. So, its signature is play(Game Grid, Symbol, x, y).

    The play method can return one of two types of values. It could return an error code indicating whether the move was legal or illegal. Or it could return the state of the game after the move — that is, Win, Draw, or Active. Just assume that play will return a Boolean, either true for success or false for failure (for example, if someone tried to play a square that was already filled). You also create three additional methods that will indicate whether the game ended with a win or a draw, or if it's still active.

  • The second method in Game is checkResult. It needs to examine the Grid, so its signature is checkResult :Grid. You want it to set the state of the game to a Win (for the player who just played), to a Draw, or to Active. You also want it to set the state of the game, so rename this method checkResultAndSetState :Grid.
  • You need methods that will return the state of the game, so add three methods: isActive, isWon, and isDrawn to the class.

Going systematically through all the classes gives you this set of methods and signatures (I’ve used a notation similar to that of Objective-C):

  • Game:
    • (Bool) play Grid: Symbol: x: y: Returns success or failure.
    • checkResultAndSetState Grid: Returns nothing.
    • isActive: Returns true or false.
    • isWon: Returns true or false.
    • isDrawn: Returns true or false.
  • GameView:
    • placeSymbol Symbol: X: Y: Returns success or failure.
    • showScores PlayerOneScore: PlayerTwoScore: Returns nothing.
  • GameGrid:
    • setSymbolAtLocation Symbol: x: y: Returns nothing.
    • Symbol getSymbolAtLocation x: y: Returns success or failure.
    • isRowFilled row: Returns YES if a row is filled with the same symbol.
    • isColumnFilled, column: Returns YES if a column is filled with the same symbol.
    • isLeftToRightDiagonalFilled: Returns YES if the left-to-right diagonal (from 0,0 to 2,2) is filled with the same symbol.
    • isRightToLeftDiagonalFilled: Returns YES if the right-to-left diagonal (from 0,2 to 2,0) is filled with the same symbol.
    • getEmptySquares: Returns a list of (x, y) coordinates of the empty squares.
  • Symbol:
    • None
  • GameSession:
    • playNewGame: Returns nothing.
    • quit: Returns nothing.
    • accumulateScores WinningPlayer: Returns nothing.

So, there they are — classes and methods with method signatures. You ask, “Am I done with designing? Can I start implementing?” My answer is, unfortunately, not yet, as I explain next.

If you were the Paladin of computing (Have Compiler, Will Travel) developing an application from scratch, you might be able to start writing code. But, sad to say, you aren't. Instead, it's time to put on the mantle of a modern-day "Knight in iOS Armor" and fit your design within the iOS framework. Namely, you need to take advantage of the framework where possible while also compromising where the framework doesn’t quite fit your design (or at least not without lots of extra work). In the next section, you see an example of how to convert your initial OO design to one that fits into the iOS Framework.

Implementing an Object-Oriented Design on iOS

If you've been following along, you now have a good OO design; however, it isn’t a design that will work on iOS. In this section, you find out how to transform your initial OO design into one that works with iOS.

First, in Xcode, open the Tic-Tac-Toe project associated with this chapter so that you can follow along as I explain how to implement it (refer to Chapter 5 for instructions on loading an existing project).

The core pattern within any iOS app is the Model-View Controller-View (as I explain in Chapters 4 and 6, this is Apple’s take on the tried-and-true Model-View-Controller, or MVC, pattern). In this pattern, views are the user-interface elements of the application. Models represent the domain logic of the application, and view controllers stitch the two together.

technicalstuff.eps Some developers see the model as only the data that an application manages, whereas I see it as the application's domain model — it includes the data as well as the application's core behavior. In this respect, the Tic-Tac-Toe game is the model, not just the grid within the game.

Because the model will likely be the most stable part of a program, implementing at least its core parts early will speed up the rest of the process.

As I explain in Chapters 4 and 6, when you create the model, you do so from the requirements or other descriptions of the app. You then validate the model by walking through it using scenarios that describe how the app is intended to be used.

Implementing the model

Now that you know what a model is, it's time to create one. To identify the classes in the model, look at the list of classes you identified during the OO design process (as discussed in the earlier section, “Illustrating Object-Oriented Design”) and identify the subset of classes that are core to the app. Incidentally, model classes won't have display or user-interface elements. The core elements of Tic-Tac-Toe are Game, Grid, and Symbol. These taken together implement the domain logic of Tic-Tac-Toe, and so make up the model.

Take a look at how each of the model classes appears when implemented, starting with the largest and most important class in the model — the Game class (called TTTGame in the app, per the iOS convention of keeping class names unique in the absence of namespaces).

Here is the interface file of the Game class (TTTGame.h):

  //
//
...
#import <Foundation/Foundation.h>
#import "TTTSymbol.h"
#import "TTTGameGrid.h"

typedef enum {
    Inactive, Active, Won, Draw
} STATE;

typedef enum {Player1, Player2} PLAYER;

@interface TTTGame : NSObject{
    @private STATE gameState;
    @private TTTSymbol *currentSymbol;
    @private PLAYER currentPlayer;
    @private PLAYER winningPlayer;
    @private NSString *PlayerOneName, *PlayerTwoName;
    @private TTTGameGrid *gameGrid;
    @private int playCount;
}
    -(id) init;
    - (TTTGameGrid *) getGameGrid;
    - (void) setPlayerNames :(NSString *) FirstPlayer
                            :(NSString *) SecondPlayer;
    - (NSString *) getPlayerOneName;
    - (NSString *) getPlayerTwoName;
    - (NSString *) getCurrentPlayerName;
    - (NSString *)  getWinningPlayerName;
    - (TTTSymbol *) getCurrentSymbol;
    - (void) checkResultAndSetState;
    - (BOOL) play :(int) x :(int) y;
    - (BOOL) isActive;
    - (BOOL) isWon;
    - (BOOL) isDrawn;
    - (int) getPlayCount;
@end

The methods in the design are almost identically reflected in the implementation. The differences between the methods in the design and the methods in the implementation are as follows:

  • Several extra methods are in the implementation.
  • The signature of the play method in the actual code is different from the signature in the design.
  • There is a method called init.

To find out why those differences exist, begin by determining why all the accessor methods are there. Notice that the Game class doesn't have visual elements and display responsibilities, which is exactly right for a model class. The views, however, need to display the Tic-Tac-Toe grid and the play-by-play progress of the game. Therefore, the view controllers need to pass the symbol currently being placed, the names of the players, and the grid from the Game class on to the views. To enable the view controllers to pass the necessary data to the views, accessor methods are provided for the model classes.

Now in the play method in the implementation has 2 parameters (the x and y coordinates) while its counterpart in the had 4 parameters (grid, symbol and the coordinates x and y).

grid isn't needed because it's a member variable of the game and doesn’t need to be passed in. symbol is also not needed. A quick look at the following implementation of play reveals why symbol doesn't need to be passed as a parameter. The implementation of the Tic-Tac-Toe game always uses X for the starting symbol (for Player One) and O for Player 2’s symbol. Therefore, you can hardwire the code to always start with X and then alternate between O and X. Note that although the method becomes simpler, the class becomes a little more complex because it "secretly" uses a characteristic of Tic-Tac-Toe.

  - (BOOL) play :(int) x :(int) y {
    BOOL successfulPlay=false;
    if ([gameGrid getSymbolAtLocation :x :y] == [TTTSymbol SymbolBlankCreate]){
        successfulPlay = true;
        playCount++;
        [gameGrid setSymbolAtLocation :x :y :currentSymbol];
        [self checkResultAndSetState];
        if(gameState == Active){// if the game is still active
            // Swap symbols and players
            if(currentSymbol == [TTTSymbol SymbolXCreate]){
                currentSymbol= [TTTSymbol SymbolOCreate];
            } else {
                currentSymbol= [TTTSymbol SymbolXCreate];
            }
            if(currentPlayer == Player1) currentPlayer = Player2;
            else currentPlayer = Player1;
    }
    return successfulPlay;
}

You may be wondering whether having extra accessors, particularly the gameGrid accessor, is a bad thing. And is incorporating a secret in your code poor design? If so, just hold those questions until I discuss them and other design decisions in the section, “Analyzing the OO and Design Principles Used in Tic-Tac-Toe,” later in this chapter.

The final difference is the method called init. This method initializes the game — creates a GameGrid, sets the state of the game to Active, the current player to Player1, and the current symbol to an X. Here is the code for init:

  -(id) init { //Constructor
    gameGrid = [[TTTGameGrid alloc] init];
    gameState = Active;
    currentSymbol = [TTTSymbol SymbolXCreate];
    currentPlayer = Player1;
    return self;
}

It's time to move on to the Game Grid class, called TTTGameGrid in the implementation. Here's its interface:

  //
//  TTTGameGrid.h
...
#import <Foundation/Foundation.h>
#import "TTTSymbol.h"

#define GAMEGRIDSIZE 3

@interface TTTGameGrid : NSObject{
    @private TTTSymbol *grid[GAMEGRIDSIZE][GAMEGRIDSIZE];
  
}
    -(id) init;
    -(void) setSymbolAtLocation: (int) x :(int) y :(TTTSymbol *) value;
    -(TTTSymbol *) getSymbolAtLocation: (int) x :(int) y;
    -(BOOL) isRowFilled: (int) row;
    -(BOOL) isColumnFilled: (int) column;
    -(BOOL) isLeftToRightDiagonalFilled;
    -(BOOL) isRightToLeftDiagonalFilled;
@end

The methods of the Game Grid class methods are exactly as designed. The implementation of the class (in TTTGameGrid.m) isn't complicated either, which is why the method implementations aren't inserted here. Just for grins, though, here’s checkResultAndSetState, the most complex method in TTTGameGrid:

  - (void) checkResultAndSetState{
    if([gameGrid isRowFilled:0]||
        [gameGrid isRowFilled:1]||
        [gameGrid isRowFilled:2]||
        [gameGrid isColumnFilled:0]||
        [gameGrid isColumnFilled:1]||
        [gameGrid isColumnFilled:2]||
        [gameGrid isLeftToRightDiagonalFilled]||
        [gameGrid isRightToLeftDiagonalFilled]){
            winningPlayer = currentPlayer;
            gameState = Won;
    }else if (playCount==9){
        gameState = Draw;
    } /* else, leave state as is */
}

checkResultAndSetState and the other methods in GameGrid are pretty straightforward. Although the methods aren't complex, GameGrid clearly illustrates the concept of abstraction and information hiding. For example, you can change the implementation of GameGrid without the classes outside it — like Game — having to change how they use GameGrid. (You see more on this and other OO design points in the section, “Analyzing the OO and Design Principles Used in Tic-Tac-Toe,” later in this chapter.)

Now, move on to Symbol. Here is the interface file of the Symbol class (named TTTSymbol):

  //  TTTSymbol.h
...
#import <Foundation/Foundation.h>

#define MARKBLANK 0
#define MARKX 1
#define MARKO 2

@interface TTTSymbol : NSObject{
    @private int value;
}
    +(TTTSymbol*) SymbolXCreate;
    +(TTTSymbol*) SymbolOCreate;
    +(TTTSymbol*) SymbolBlankCreate;
    -(NSString *) toString;
    -(UIImage *)  getBitmapForSymbol;
@end

This class encapsulates the Tic-tac-Toe symbols X and O. Note the two variations of the Singleton pattern applied in the implementation of this class.

  • One variation is in the factory methods for the symbol instances SymbolBlankCreate, SymbolXCreate, and SymbolOCreate.
  • The second variation of the Singleton pattern is in getBitmap
ForSymbol.

    You find more on using the Singleton pattern in the section, “Analyzing the OO and Design Principles Used in Tic-Tac-Toe,” later in this chapter.

The last class in the Tic-Tac-Toe model is Square (named TTTSquare). Here is its interface file:

  //
...
//
#import <Foundation/Foundation.h>

@interface TTTSquare : NSObject{
    @private int x;
    @private int y;
}
    -(id) initWithXY: (int) initX :(int) initY;
    -(int) x;
    -(int) y;
@end

You use this class as a data structure to represent a square within the game grid. You also use this class to represent each square that is returned when a collection of the empty squares in the grid is requested. This class implements a pattern known as Data Transfer Object, which I explain in the last section of this chapter.

Now that the model is ready, you can build the rest of the application. So read on!

Creating storyboards and views

One of the very convenient features Apple added to Xcode 4 is the Storyboard feature, which is a declarative (that is a non-programming) means of specifying (but not implementing) the screen flow of an app. The Storyboard feature can also be used to define most of the views of the app. A storyboard consists of a representation of the screens in an app and the transitions between them. The individual screens are called scenes, and the transitions between the screens are called segues.

For more on how to work with storyboards, scenes, and segues, check the Storyboards and Scenes link in the web resources for the chapters in this book at www.dummies.com/go/iosprogramminglinks.

As you can see from the screen flow diagram of the app (refer to Figure 7-4), the Tic-Tac-Toe app transitions between two main states:

  • A user is about to start a game session (refer to Figure 7-1).
  • The players are playing a Tic-Tac-Toe game in an active game session (refer to Figure 7-2).

These states also correspond to the start and end of Use Case 1. These two screens will be the basis of the two scenes in the storyboard of the app, with a segue named segueToGameSession from Scene 1 to Scene 2. You can see all of this in Figure 7-5.

Within each scene are views. Scene 1 is the main screen of the app, and it has two buttons, Start a New Tic-Tac-Toe Session and Exit. The gameplay screen (Scene 2) shows a drawing area for the Tic-Tac-Toe board (highlighted in blue in Figure 7-6) and two text fields where the status of the game is displayed.

9781118799277-fg0705.tif

Figure 7-5: Storyboard for Tic-Tac-Toe showing the scenes and a segue.

9781118799277-fg0706.tif

Figure 7-6: Scene 2 in the Tic-Tac-Toe storyboard.

In Figures 7-1 and 7-2, shown earlier in this chapter, you can see that a good deal of the Tic-Tac-Toe user interface is done declaratively. Of course, you'll need more code to complete the implementation of the views and to orchestrate the flow of the user interface from scene to scene, which you read about in the next section.

Making the app active

So far in this section, you've read about the app's model and should have a good idea about what the app's user interface look like (that is, its views). But views and a model doth not an app make. View controllers must stitch models and views together and make the app actually work.

remember.eps The view controllers are where the app's use cases are implemented.

Following the object-oriented design principle of cohesion, I created separate view controllers for the two scenes. (By cohesion, I mean ensuring that the things a class does relate to each other and to the intent of the class. Refer to Chapter 2 for more on cohesion.)

I call these two view controllers the Game Options view controller and the Game Session view controller. Both view controllers are subclasses of UIViewController, the base class in the iOS Framework from which all view controllers are derived. Note that they also bound the classes of these view controllers to their respective scenes through the Identity inspector in Xcode’s Utilities area (refer to Chapter 5). Also, events in these scenes are bound to actions in their respective view controllers.

You implement the Game Options view controller (class name TTTGameOptionsViewController). The Game Options view controller is also designated the root view controller, and the iOS runtime launches it when the app starts up. Along with this view controller, the view it manages (that is, Scene 1) is also created and presented.

The primary role of the Game Options view controller is to implement the first part of “Use Case 1: Player starts a game session.” As a result, the Touch Down event in the Start a New Tic-Tac-Toe Session button is bound to the IBAction annotated startNewGame method (see TTTGameOptionsViewController.m). Clicking Start a New Tic-Tac-Toe Session invokes this method, as shown here:

  - (IBAction)startNewGame{
    [self performSegueWithIdentifier:@"segueToGameSession" sender:self];
}

startNewGame is known as an action method because its purpose is to respond to a user action, such as touching a button (I explain action methods in Chapter 9). The method is simplicity itself. It just segues the app from Scene 1 to Scene 2, causing the Game Session view to come up, showing the Tic-Tac-Toe grid and the two status display areas. This action launches the Game Session view controller (implemented by the class TTTGameSessionViewController) along with its view.

The Game Session view controller is the workhorse of the Tic-Tac-Toe app because it implements the remaining part of Use Case 1 and all the other use cases. Its view comprises three parts:

  • A board where the Tic-Tac-Toe grid and the played Xs and Os are shown
  • A text field that shows the game status (either the next player or the final result)
  • A text field that shows the accumulated scores of the two players in the current session

Figure 7-7 shows the views that the Game Session view controller manages.

9781118799277-fg0707.tif

Figure 7-7: View managed by the Game Session view controller.

The board is a custom view that's implemented by the TTTBoard class, which is a subclass of UIView. Here is the interface file for the TTTBoard class:

  //
//
...
#import <UIKit/UIKit.h>
#import "TTTGameSessionViewController.h"

@class TTTGameGrid;

@interface TTTBoard : UIView{
    @private BOOL enabled;
    @private TTTGameSessionViewController *gameSession;
    @private TTTGameGrid *grid;
    @private float width;         // width of one block
    @private float height;       // will be same as width;
}
    - (void)drawRect:(CGRect)rect;
    - (void) setGrid: (TTTGameGrid *) aGrid;
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
    - (void) setGameSession: (TTTGameSessionViewController *) aGameSession;
    - (float) getWidth;
    - (float) getHeight;
    - (void) invalidateBlock: (int) x :(int) y;
    - (BOOL) isInputEnabled;
    - (void) disableInput;
    - (void) enableInput;
@end

Next, I explain what the important methods in this class do, and how they work:

  • TTTBoard is specialized from UIView in that it overrides the drawRect method from UIView and implements its own method. This drawRect method is the one that draws the Tic-Tac-Toe grid along with all the played symbols when the board changes (that is, after each move).

    technicalstuff.eps The drawRect method is automatically called by the iOS runtime system or, more specifically, by the view manager within iOS when the view needs to be rendered.

  • TTTBoard implements several of its own methods, such as accessor methods to get its height, width, and status, to set its grid and view controller and to enable and disable input.
  • TTTBoard overrides and implements its own touchesBegan method in order to respond to the placement of symbols.

Via the storyboard of the app, this board is wired to and can be referenced by the outlet boardView in the Game Session view controller. The two text fields in this view are wired to (and therefore can be referenced by) the outlets turnTextField and scoreTextField in the Game Session view controller. These three outlets are declared in TTTGameSessionViewController:

  @property (nonatomic, retain) IBOutlet TTTBoard *boardView;
@property (nonatomic, retain) IBOutlet UITextField *scoreTextField;
@property (nonatomic, retain) IBOutlet UITextField *turnTextField;

Here's what the Game Session view controller does:

  1. The iOS runtime launches the Game Session view controller when Scene 1 segues to Scene 2.
  2. The iOS runtime calls the viewDidLoad method of Game Session after all its views are successfully loaded and rendered, as shown here:

      - (void)viewDidLoad{
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        [self initializeGameSession];
        [self playNewGame];
    }

  3. viewDidLoad, in turn, calls initializeGameSession (which initializes the game session) and playNewGame, which sets up a new game.

    The game is event driven in that the Tic-Tac-Toe program sets up and displays the board and then responds to the placement of symbols. This response begins with the touchesBegan method of TTTBoard, as shown here:

      - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
      
        [super touchesBegan:touches withEvent:event];
          
        NSSet* touchesInView = [event touchesForView:self];
        UITouch* touchInGrid = (UITouch *)[touchesInView anyObject];
        CGPoint touchPoint = [touchInGrid locationInView:self];
        float x = touchPoint.x-TTTBOARDLOCATIONINVIEWX;
        float y = touchPoint.y-TTTBOARDLOCATIONINVIEWX;

        [gameSession boardTouchedAt:x :y];
    }

  4. touchesBegan receives the touch event, extracts the window coordinates of the touch points in the event (touchPoint.x and touchPoint.y), translates them to grid coordinates (x and y), and passes these grid coordinates to the Game Session view controller via the controller’s boardTouchedAt method.
  5. The boardTouchedAt method translates the touch point to Tic-Tac-Toe grid coordinates and calls humanTakesATurn, which calls methods of the Game class (named TTTGame) to execute the logic behind a move.

    This logic is pretty straightforward. Once the move is made, methods in the Game class check to see whether the move causes a row, column, or diagonal to be filled with the same symbol:

    • If so, the game is terminated as a victory for the player making the last play.
    • If not, these methods check to see whether the board is filled.

      If the board is filled, the game is declared a draw.

If the game is won or there's a draw, the game terminates (its state is set to won or drawn, as the case may be). Game Session displays appropriate messages and asks whether another game should be started, in which case the cycle begins anew at playNewGame.

Finally, if neither of these cases is true, the game stays active and simply waits for the next move.

Analyzing the OO and Design Principles Used in Tic-Tac-Toe

I created the Tic-Tac-Toe application to illustrate object-oriented (OO) techniques and principles as well as the process of OO design and development. So I'll analyze it from that perspective. (This analysis is arranged according to the order of the OO concepts in Chapter 2.)

  • Language constructs, such as classes, objects, inheritance, methods, and polymorphism
  • Principles of OO design, namely, information hiding, low coupling, and high cohesion
  • Techniques, such as delayed binding, delegation
  • Patterns
  • Frameworks, specifically the iOS framework

Tic-Tac-Toe uses many of the building blocks of OO as implemented in Objective-C. The program uses these classes: Board, Game, Game Grid, Square, and Symbol, and two view controllers — Game Options view controller and Game Session view controller (note that the exact class names have no spaces and are prefixed with TTT). Objects of each of these classes are instantiated, and the game is implemented as collaborations among these objects, manifested as messages being sent between objects that result in invocations of their methods.

Each of the classes inherits from a base class in the iOS framework — Game, Game Grid, Square, and Symbol from NSObject, Board from UIView, and the two view controllers from UIViewController. Considering this inheritance, you should expect to see examples of polymorphic behavior in Tic-Tac-Toe, and it doesn’t disappoint. Here are two examples:

  • The Board class (named TTTBoard) inherits from UIView.

    Board overrides the drawRect that it inherits from UIView. This view is rendered by the iOS framework (specifically, the iOS window manager), which recognizes this view as an instance of UIView. However, the (custom) drawRect method is invoked.

  • touchesBegan is another method from UIView that's overridden in Board.

    Here again, the iOS window manager thinks it's dealing with an instance of a UIView class, whereas the object is actually an instance of Board.

Using objects only through their interface enforces a certain degree of information hiding. By accessing objects only through their interface, the using objects know no more than the externally accessible methods and their signatures of the used objects. See how each class in Tic-Tac-Toe, for the most part, can be understood by its interface alone. However, Objective-C does force you to reveal the member variables of a class in an interface file, which doesn't unnecessarily need to be revealed. In general, the OO design process also facilitates proper separation of class responsibilities and enables low coupling among classes and high cohesion within each class.

One interesting example of separation of concerns within the app (but which you don’t see in the design process) is the new memory management paradigm in iOS — Automated Reference Counting (ARC). Thanks to this paradigm, an object doesn't have to worry about how its collaborating objects are dealing with shared information.

Tic-Tac-Toe also uses delegation. For example, the Start a Tic-Tac-Toe Session button in Scene 1 is delegating handling of the Touch Down button press event to the method startNewGame in the Game Options view controller. The framework also uses delegation. For example, it delegates the start up of the app to an instance of an app-specific class — the app’s own Applicaton Delegate class (named TTTAppDelegate in the file main.m).

  //
...
//
...

#import <UIKit/UIKit.h>

#import "TTTAppDelegate.h"

int main(int argc, char * argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([TTTAppDelegate class]));
    }
}

Use of design patterns in Tic-Tac-Toe

Design patterns are formal ways of documenting a solution to a design problem (I discuss design patterns in Chapter 4). Tic-Tac-Toe uses two patterns: Singleton and Model-View-Controller.

Singleton pattern

The Singleton pattern is used in the Symbol class. Note how I customized this pattern in the Tic-Tac-Toe application:

  • Rather than the one instance returned by the textbook Singleton pattern, three instances (one each for the X, O, and blank symbols) are allowed in the Symbol class. These instances are managed by the three static, or class, methods. One of these methods is shown here:

      +(TTTSymbol*) SymbolXCreate{
       @synchronized([TTTSymbol class]){
           if (SymbolX == nil){
               SymbolX = [[TTTSymbol alloc] init];
               SymbolX->value = MARKX;
           }
           return SymbolX;
       }
    }

  • The technique used in the Singleton pattern to create only one instance is used in the method getBitmapForSymbol to get the images for the X, O, and blank symbols:

      - (UIImage *) getBitmapForSymbol{
            @synchronized([TTTSymbol class]){
                if (!bitMapsInitialized){
                    NSString* imagePath =
                        [[NSBundle mainBundle]
                            pathForResource:
                                @"Images.bundle/x" ofType:@"png"];
                    imageX = [[UIImage alloc]
                        initWithContentsOfFile:imagePath];
                    imagePath =
                        [[NSBundle mainBundle]
                            pathForResource:
                                @"Images.bundle/o" ofType:@"png"];
                    imageO =
                        [[UIImage alloc] initWithContentsOfFile:imagePath];
                    imagePath =
                        [[NSBundle mainBundle]
                            pathForResource:
                                @"Images.bundle/blank" ofType:@"png"];
                    imageBlank =
                        [[UIImage alloc] initWithContentsOfFile:imagePath];
                    bitMapsInitialized=true;
                }
            }
            UIImage *imageSelected = imageBlank;
      
            if (self == [TTTSymbol SymbolXCreate]) imageSelected = imageX;
            else if (self == [TTTSymbol SymbolOCreate])
                imageSelected = imageO;
            return imageSelected;
        }

  • Because iOS apps can be multithreaded, Singleton is modified to deal with multiple threads by using the @synchronized annotation.

Model-View-Controller pattern

Model-View-Controller (MVC), shown in Figure  7-8, is the most important pattern within iOS. It's frequently used in applications, particularly web applications.

The pattern isolates the domain logic and core objects of the application (aka the model) from the application’s user interface. In this way, these important components (that is, the model and the views) can be designed, implemented, and maintained separately. The controller is placed between the model and the user interface. It receives user actions (such as The user clicked here) and translates those commands into actions on the model and then takes the resulting model updates and notifies the user interface to update itself.

9781118799277-fg0708.tif

Figure 7-8: The Model-View-Controller design pattern.

Within iOS, controllers are called view controllers.

The Tic-Tac-Toe model consists of the classes Game, Grid, and Symbol. These classes encapsulate the domain logic of the game.

technicalstuff.eps The Game, Grid, and Symbol classes are implemented so that they're completely independent of the application's user interface. You could use these classes just as they are in a command-line–driven, console-app version of Tic-Tac-Toe. Note that the logic of a game session is currently embedded in the Game Session view controller. You could also abstract a class representing a game session and make it part of the model.

The two major views in Tic-Tac-Toe are represented by Scene 1 and Scene 2 in the Tic-Tac-Toe storyboard (refer to Figure  7-5). Objects that represent these views are instances of UIView and are created behind the scenes (no pun intended) by the iOS runtime. The buttons, text fields, and Board class that represent the Tic-Tac-Toe grid are subordinate views within these main views.

Finally, note that the Game View class (named TTTGameView and implemented in the files TTTGameView.h and TTTGameView.m) is also a view. It encapsulates the user-interface elements in Scene 2 (the board and the two text fields that show the scores and the game status, respectively).

Corresponding to the two scenes are two view controllers in Tic-Tac-Toe: the Game Options view controller and the GameSession view controller. (Refer to the previous section where I explain how these two view controllers manage the app's logic and tie together the model objects and the views.)

Data Transfer Object pattern

The Data Transfer Object (DTO) pattern is used when several bits of information that need to be transferred from one part of the system to another part are encapsulated into an object. You see the DTO pattern in the method getEmptySquares, defined in the Game Grid class and used to return the list of empty squares on the board. Every instance of the Square class (see TTTSquare.m and the corresponding interface file TTTSquare.h) is a DTO.

Façade pattern

You use the Façade pattern to simplify the use of the functionality provided by a set of classes. A façade provides a simpler set of methods to interact with these classes. Rather than you having to learn how to use the individual classes, a program can use these classes through a façade. You see an example of a façade in the Game View class, which simplifies the interaction between the Game Session view controller and the Scene 2-based view it handles.

Other concepts

Other, mostly language-specific, examples of OO concepts are utilized. A simple example of abstraction and loose coupling shows up in the use of #define and #typedef constructs to enumerate the values allowed by a variable. You can see #typedef constructs in the definition and State and Player types in Game. Here's the code from TTTGame.h that defines the State type (named STATE) and the Player type (PLAYER):

  typedef enum {
    Inactive, Active, Won, Draw
} STATE;

typedef enum {Player1, Player2} PLAYER;

The enum construct also provides type safety, as opposed to an approach that might use integer constants to represent both player and state.

Here's an example of #define statements used to define the size and location of the board drawing area (from TTTBoard.h):

  #define TTTBOARDLOCATIONINVIEWX 10
#define TTTBOARDLOCATIONINVIEWY 10
#define TTTBOARDSIZE 200

By using #defines, the size of the board and its location on the window are defined in one place in the file. You can modify these values simply by editing these constants, rather than by changing the values in all the places in the code where they're used.

As you begin to understand the Tic-Tac-Toe code, you’ll see tradeoffs, compromises, and even flaws in the OO design. Some of these, such as the visibility of member variables in the interface files, are a consequence of using Objective-C.

Other compromises are made by the iOS framework. For example, some actions on events are implemented by delegate objects (such as the callbacks to button clicks). However, rather than implement delegates, you can implement some events by overriding base classes and using inheritance-driven polymorphism, such as processing touch events on the Board and drawing of the board, both done by overriding methods – touchesBegan and drawRect, respectively.

I made other compromises; I list them here with the intention of starting a discussion about them:

  • The Game Session view controller handles all three subviews — the Board and the two text fields. This view controller handles the game session logic and the game logic. Could I (and should I) split the responsibility of this controller with another? For example, would it be better to create an additional view controller to handle the playing of a single game?
  • Game View is only a partial façade over the views that the Game Session view controller handles. It abstracts the text fields from the following:
    • The Game Session view controller (via setGameStatus and showScores)
    • The symbol placement on the Board (via placeSymbol)

    However, the Board is coupled directly to the Game Session view controller in how it responds to events — such as touches. Take a look at how the touchesBegan method, shown here, directly invokes boardTouchedAt on gameSession:

           - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
      
                [super touchesBegan:touches withEvent:event];
          
                NSSet* touchesInView = [event touchesForView:self];
                UITouch* touchInGrid = (UITouch *)[touchesInView anyObject];
                CGPoint touchPoint = [touchInGrid locationInView:self];
                float x = touchPoint.x-TTTBOARDLOCATIONINVIEWX;
                float y = touchPoint.y-TTTBOARDLOCATIONINVIEWX;

                [gameSession boardTouchedAt:x :y];

            }

    In order for Game View to be a complete façade over the game play views, Game View should take care of the event handling, as well. Would doing this make the components of the program more cohesive and easier to understand? Or would the addition of another layer of indirection make understanding the program more difficult?

  • The Game class is subtly coupled with the Board class because an instance of Board and an instance of Game from within the same game share the same Game Grid instance.

    The following lines from playNewGame within the Game Session view controller illustrate this point:

          - (void) playNewGame{
            activeGame = [[TTTGame alloc] init];
            . . .
            TTTGameGrid *gameGrid = [activeGame getGameGrid];
            [boardView setGrid:gameGrid];
          
            . . .
        }

    Here, Board needs the Game Grid to redraw itself and Game needs the same Game Grid to play the game. You could also hold the Game Grid instance in the Game class and provide accessor methods in the Game class to access the cells. I see good and bad points for both approaches. If you like, try this alternative approach and see whether it works for you.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.17.28