Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert this procedural programming to object-oriented programming?

I have a source code that is needed to be converted by creating classes, objects and methods. So far, I've just done by converting the initial main into a separate class. But I don't know what to do with constructor and which variables are supposed to be private. This is the code :

import java.util.*;

public class Card{

private static void shuffle(int[][] cards){

    List<Integer> randoms = new ArrayList<Integer>();
    Random randomizer = new Random();

    for(int i = 0; i < 8;) {
      int r = randomizer.nextInt(8)+1;
      if(!randoms.contains(r)) {
        randoms.add(r);
        i++;
      }
    }

    List<Integer> clonedList = new ArrayList<Integer>();
    clonedList.addAll(randoms);
    Collections.shuffle(clonedList);
    randoms.addAll(clonedList);
    Collections.shuffle(randoms);

    int i=0;

    for(int r=0; r < 4; r++){
        for(int c=0; c < 4; c++){
            cards[r][c] = randoms.get(i);
            i++;
        }
    }
}

public static void play() throws InterruptedException {

    int ans = 1;
    int preview;
    int r1,c1,r2,c2;
    int[][] cards = new int[4][4];
    boolean[][] cardstatus = new boolean[4][4];
    boolean gameover = false;
    int moves;
    Scanner input = new Scanner(System.in);

    do{
        moves = 0;

        shuffle(cards);

        System.out.print("Enter the time(0 to 5) in seconds for the preview of the answer : ");
        preview = input.nextInt();

        while((preview<0) || (preview>5)){
            System.out.print("Invalid time!! Re-enter time(0 - 5) : ");
            preview = input.nextInt();
        }

        preview = 1000*preview;
        System.out.println(" ");

        for (int i =0; i<4;i++){
            for (int j=0;j<4;j++){

                System.out.print(cards[i][j]);
                System.out.print(" ");
            }
            System.out.println("");
            System.out.println("");
        }

        Thread.sleep(preview);

        for(int b=0;b<25;b++){
            System.out.println(" ");
        }

        for(int r=0;r<4;r++){
            for(int c=0;c<4;c++){
                System.out.print("*");
                System.out.print(" ");
                cardstatus[r][c] = false;
            }
            System.out.println("");
            System.out.println(" ");
        }

        System.out.println("");

        do{
            do{
                System.out.print("Please insert the first card row : ");
                r1 = input.nextInt();
                while((r1<1) || (r1>4)){
                    System.out.print("Invalid coordinate!! Re-enter first card row : ");
                    r1 = input.nextInt();
                }

                System.out.print("Please insert the first card column : ");
                c1 = input.nextInt();
                while((c1<1) || (c1>4)){
                        System.out.print("Invalid coordinate!! Re-enter first card column : ");
                        c1 = input.nextInt();
                }

                if(cardstatus[r1-1][c1-1] == true){
                    System.out.println("The card is already flipped!! Select another card.");
                    System.out.println("");
                }
            }while(cardstatus[r1-1][c1-1] != false);

            do{
                System.out.print("Please insert the second card row : ");
                r2 = input.nextInt();
                while((r2<1) || (r2>4)){
                    System.out.print("Invalid coordinate!! Re-enter second card row : ");
                    r2 = input.nextInt();
                }

                System.out.print("Please insert the second card column : ");
                c2 = input.nextInt();
                while((c2<1) || (c2>4)){
                    System.out.print("Invalid coordinate!! Re-enter second card column : ");
                    c2 = input.nextInt();
                }

                if(cardstatus[r2-1][c2-1] == true){
                    System.out.println("The card is already flipped!! Select another card.");
                }
                if((r1==r2)&&(c1==c2)){
                    System.out.println("You can't select the same card twice!!");
                    continue;
                }
            }while(cardstatus[r2-1][c2-1] != false);

            r1--;
            c1--;
            r2--;
            c2--;

            System.out.println("");
            System.out.println("");
            System.out.println("");

            for(int r=0;r<4;r++){
                for(int c=0;c<4;c++){

                    if((r==r1)&&(c==c1)){
                        System.out.print(cards[r][c]);
                        System.out.print(" ");
                    }
                    else if((r==r2)&&(c==c2)){
                        System.out.print(cards[r][c]);
                        System.out.print(" ");
                    }
                    else if(cardstatus[r][c] == true){
                        System.out.print(cards[r][c]);
                        System.out.print(" ");
                    }
                    else{
                        System.out.print("*");
                        System.out.print(" ");
                    }
                }
                System.out.println(" ");
                System.out.println(" ");
            }

            System.out.println("");

            if(cards[r1][c1] == cards[r2][c2]){
                System.out.println("Cards Matched!!");

                cardstatus[r1][c1] = true;
                cardstatus[r2][c2] = true;
            }
            else{
                System.out.println("No cards match!!");
            }

            Thread.sleep(2000);

            for(int b=0;b<25;b++){
                System.out.println("");
            }

            for(int r=0;r<4;r++){
                for(int c=0;c<4;c++){
                    if(cardstatus[r][c] == true){
                        System.out.print(cards[r][c]);
                        System.out.print(" ");
                    }
                    else{
                        System.out.print("*");
                        System.out.print(" ");
                    }
                }
                System.out.println("");
                System.out.println(" ");
            }

            System.out.println("");
            System.out.println("");
            System.out.println("");

            gameover = true;

            for(int r=0;r<4;r++){
                for( int c=0;c<4;c++){
                    if(cardstatus[r][c]==false){
                        gameover = false;
                        break;
                    }
                }
                if(gameover==false){
                    break;
                }
            }

            moves++;

        }while(gameover != true);

        System.out.println("Congratulations, you won!!");
        System.out.println("It required " + moves + " moves to finish it.");
        System.out.println("");
        System.out.print("Would you like to play again? (1=Yes / 0=No) : ");
        ans = input.nextInt();

    }while(ans == 1);


}


}

The main class is:

import java.util.*;

public class PlayCard{

public static void main(String[] args) throws InterruptedException{

    Card game = new Card();
    game.play();

    }
}

Should I simplify the Card class by creating other classes?

Through this code, my javadoc has no constructtor. So i need help on this!

like image 243
keitamike Avatar asked Mar 18 '10 13:03

keitamike


People also ask

What is procedural programming in object-oriented programming?

The focus of procedural programming is to break down a programming task into a collection of variables, data structures, and subroutines, whereas in object-oriented programming it is to break down a programming task into objects that expose behavior (methods) and data (members or attributes) using interfaces.

Is object-oriented programming same as procedural?

Object-oriented programming is based on the real world. Procedural programming is used for designing medium-sized programs. Object-oriented programming is used for designing large and complex programs. Procedural programming uses the concept of procedure abstraction.

Can you mix procedural and OOP?

Using both will not break your code and technically "ok", but consider future maintenance of the application by you or someone else. It's a lot easier to read the code if it's consistent and clean and you will save a lot of time later by picking and sticking to one now (preferably OOP).

How are procedural programming and object-oriented programming similar?

Object-oriented Programming uses classes and objects, Procedural Programming takes on applications by solving problems from the top of the code down to the bottom. This happens when a program starts with a problem and then breaks that problem down into smaller sub-problems or sub-procedures.


4 Answers

But I don't know what to do with constructor and which variables are supposed to be private.

Whatever can be, should be private. To determine this, it's often easiest to mark everything private, and then when you hit a "pain point" promote it to public.

As for what classes you need, take a look at the existing code for "smells" such as:

int[][] cards = new int[4][4];

You have a "named primitive"1 there - and it's used quite often. This makes it an important noun to your program. Encapsulate it into a class:

public class Cards {
   private int[][] cards = new int[4][4];
}

Now, look for where you're manipulating that named primitive:

shuffle(cards);

for (int i = 0; i < 4; i++){
   for (int j = 0; j < 4; j++){
       System.out.print(cards[i][j]);
       System.out.print(" ");
   }
   System.out.println("");
   System.out.println("");
}

Those are prime targets for methods:

public class Cards {
   private int[][] cards = new int[4][4];

   public void shuffle() {
      // existing shuffle method goes here - but works with private cards
   }

   public void print() {
      for (int i = 0; i < 4; i++){
        for (int j = 0; j < 4; j++){
           System.out.print(cards[i][j]);
           System.out.print(" ");
        }
      System.out.println("");
      System.out.println("");
   }
}

And, then - look at easy ways to generalize. Cards currently is hardcoded to a 4 x 4 board - let's parameterize that:

public class Cards {
   private int width;
   private int length;
   private int[][] cards;

   public void shuffle() {
      // existing shuffle method goes here - but works with private cards
   }

   public void print() {
      for (int i = 0; i < length; i++){
        for (int j = 0; j < width; j++){
           System.out.print(cards[i][j]);
           System.out.print(" ");
        }
      System.out.println("");
      System.out.println("");
   }
}

Now we need someone to provide the length and width - that's what constructors are for:

public class Cards {
    private int width;
    private int length;
    private int[][] cards;

   public Cards(int length, int width) {
      this.length = length;
      this.width = width;
      this.cards = new int[length][width];
   }

   public void shuffle() {
      // existing shuffle method goes here - but works with private cards
   }

   public void print() {
      for (int i = 0; i < length; i++){
        for (int j = 0; j < width; j++){
           System.out.print(cards[i][j]);
           System.out.print(" ");
        }
      System.out.println("");
      System.out.println("");
   }
}

And, then we realize that Cards isn't such a good name for this...It's more of a Board - rename it and move on to the next "named primitive" or smell2.

1 I use "named primitive" to indicate the same primitive type that is either global or passed around between methods with the same name. Since there is no class, the semantic meaning is purely in the name - often that name is a great starting point for a class. It's related to the well-known "primitive obsession" code smell, but slightly different in that it doesn't require a large number of primitive types masquerading as a class. A single named primitive can be promoted to a class.

2 A lot of the code smells are particular to OOP code. For looking at procedural code that you're trying to convert to OO, I think "primitive obsession", "data clumps", "message chains", and "long parameter lists" are the most relevant.

like image 83
Mark Brackett Avatar answered Oct 01 '22 22:10

Mark Brackett


It looks like the classic memory game, using cards. I see four main classes:

  1. Game
  2. Deck
  3. Card
  4. Grid

Here are some suggested properties and methods for each. It's incomplete, but it should get you going.

Game
  previewDuration
  selectedRow
  selectedColumn
  selectedMatchRow
  selectedMatchColumn
  deck
  grid
  main()
  promptForColumn()
  promptForRow()
  preview()
  loadGrid()

Deck
  cardCount
  shuffle()
  getNextCard()

Card
  caption
  equals()

Grid
  width
  height
  getCardAtPos()
like image 24
Marcus Adams Avatar answered Oct 01 '22 20:10

Marcus Adams


Before worrying about objects, you should first at least separate out sections into named methods so it's more clear what each part is doing. That's important whether it's OO or not. For instance:

    for (int i =0; i<4;i++){
        for (int j=0;j<4;j++){

            System.out.print(cards[i][j]);
            System.out.print(" ");
        }
        System.out.println("");
        System.out.println("");
    }

looks like it's printing out the board. Make that a separate function and call it printBoard or something similar.

As for classes, I'd probably make a Board class that has your cards and cardstatus and methods to check a current card as well probably shuffle. After you get that working I'd try to remove all input and output from the Board class.

Refactoring is rarely a single-step process. Rewriting is an option since it's a relatively small program, but be aware that you'll probably reintroduce some bugs you previously fixed and probably some new ones in the process (same thing with refactoring, but making smaller changes makes it slightly less likely)

like image 40
Davy8 Avatar answered Oct 01 '22 21:10

Davy8


This answer illustrates one possible refactoring you can do to make your approach more object-oriented. It doesn't address the problem domain (Cards, Decks, etc), which are addressed by other answers. Instead, it describes how you might use OO principles to tackle the programmatic implementation in a more OO manner.

The first step I would do is factor out what I'll call the PromptedLoop construct. You have a do/while loop in .play that prompts the user whether or not to run, and while he says yes, it does some action.

Take this functionality and encapsulate it. Make the Card a Runnable object and allow a PromptedLoop to run it repeatedly.

public interface Runnable
{
    public void Run();
}

public class PromptedLoop()
{
    private Runnable runner;
    private String prompt;

    public PromptedLoop(Runnable runner)
    {
        this.runner = runner;
        this.prompt = "Again? (1=Yes / 0=No):";
    }

    public PromptedLoop(Runnable runner, String prompt)
    {
        this.runner = runner;
        this.prompt = prompt;
    }

    public void Go()
    {
        int ans = 0;
        do
        {
            runner.Run();
            System.out.println("");
            System.out.print(prompt);
            ans = input.nextInt();
        } while(ans == 1);
    }
}

class Card
implements Runnable
{
    public void Run()
    {
        play();
    }
    ...
}

public class PlayCard {
    public static void main(String[] args) throws InterruptedException
    {
        Card game = new Card();
        PromptedLoop loop = new PromptedLoop(game, "Would you like to play again? (1=Yes, 0=No)");
        loop.Go();
    }

Then, .play can be just a single instance of a play action and doesn't have to integrate the looping functionality. You then have the option of re-using the PromptedLoop for other programs, and you can more easily read .play as you're not distracted by the fact that it might be looped over.

like image 31
Jason R. Coombs Avatar answered Oct 01 '22 20:10

Jason R. Coombs