Как преобразовать это процедурное программирование в объектно-ориентированное?

у меня есть исходный код, который необходимо преобразовать путем создания классов, объектов и методов. До сих пор я только что сделал, преобразовав начальный main в отдельный класс. Но я не знаю, что делать с конструктором, и какие переменные должны быть частные. Это код:

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);


}


}

основной класс:

import java.util.*;

public class PlayCard{

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

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

    }
}

должен ли я упростить класс карты, создав другие классы?

через этот код, мой javadoc есть не constructtor. Так мне нужна помощь!

6 ответов


но я не знаю, что делать с конструктором, и какие переменные должны быть частные.

все, что может быть, должно быть private. Чтобы определить это, часто проще всего отметить все private, а затем, когда вы нажмете "болевую точку", продвиньте ее до public.

Что касается того, какие классы вам нужны, взгляните на существующий код для "запахов", таких как:

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

у вас есть "имени примитивный"1 там - и он используется довольно часто. Это делает его важным существительным для вашей программы. Инкапсулируйте его в класс:

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

теперь посмотрите, где вы манипулируете этим названным примитивом:

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("");
}

это основные цели для методов:

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("");
   }
}

и, затем-посмотрите на простые способы обобщения. Cards в настоящее время жестко закодирован на плате 4 x 4 - Давайте параметризовать это:

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("");
   }
}

сейчас нам нужен кто-то предоставить length и width - вот для чего нужны конструкторы:

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("");
   }
}

и тогда мы осознаем, что Cards не такое уж хорошее название для этого...это скорее Board - переименуйте его и перейдите к следующему "именованному примитиву" или запах2.

1 я использую "именованный примитив", чтобы указать тот же примитивный тип, который является глобальным или передается между методами с тем же именем. Так как нет класса, смысловое значение заключается исключительно в имени-часто это имя является отличной отправной точкой для класса. Это связано с известным запахом кода "примитивная одержимость", но немного отличается тем, что не требует большого количества примитивных типов, маскирующихся под класс. Одного имени примитив может быть повышен до класса.

2 многие запахи кода характерны для кода ООП. Для просмотра процедурного кода, который вы пытаетесь преобразовать в OO, я думаю "примитивная одержимость"," сгустки данных"," цепочки сообщений "и" длинные списки параметров " являются наиболее релевантными.


это похоже на классическую игру памяти, используя карты. Я вижу четыре основных класса:

  1. - игры
  2. палуба
  3. карта
  4. решетки

вот некоторые предлагаемые свойства и методы для каждого. Он неполный, но он должен помочь тебе.

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

Deck
  cardCount
  shuffle()
  getNextCard()

Card
  caption
  equals()

Grid
  width
  height
  getCardAtPos()

прежде чем беспокоиться об объектах, вы должны сначала по крайней мере разделить разделы на именованные методы, чтобы было более ясно, что делает каждая часть. Это важно, ОО это или нет. Например:

    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("");
    }

похоже, что эти доски. Сделайте это отдельной функцией и назовите ее printBoard или чем-то подобным.

что касается классов, я бы, вероятно, сделал класс доски, который имеет ваш cards и cardstatus и методы проверки текущей карты, а также вероятно shuffle. После того, как вы получите эту работу, я попытаюсь удалить все входные и выходные данные из класса платы.

рефакторинг редко является одноступенчатым процессом. Переписывание-это вариант, так как это относительно небольшая программа, но имейте в виду, что вы, вероятно, повторно введете некоторые ошибки, которые вы ранее исправили, и, вероятно, некоторые новые в процессе (то же самое с рефакторингом, но внесение меньших изменений делает его немного менее вероятным)


этот ответ иллюстрирует один возможный рефакторинг, который вы можете сделать, чтобы сделать ваш подход более объектно-ориентированным. Он не обращается к проблемной области (карты, колоды и т. д.), которые рассматриваются другими ответами. Вместо этого он описывает, как можно использовать принципы OO для решения программной реализации более OO способом.

первый шаг я сделать, это фактор, что я позвоню в PromptedLoop построить. У вас есть цикл do/while .воспроизведение, которое запрашивает пользователя, является ли или нет бежать, и пока он говорит "Да", это делает какое-то действие.

возьмите эту функциональность и инкапсулируйте ее. Сделайте карту управляемым объектом и разрешите PromptedLoop запускать ее повторно.

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();
    }

затем .игра может быть только одним экземпляром действия игры и не должна интегрировать функциональность цикла. Затем у вас есть возможность повторно использовать PromptedLoop для других программ, и вы можете более легко читать .играйте так, как вас не отвлекает тот факт, что это может быть проциклили.


к сожалению, лучший (единственный) способ конвертировать "процедурную" программу в "OO" - это построить ее с нуля.

создайте объектную модель и функциональность, которую, по вашему мнению, должен иметь каждый объект, и напишите классы и заглушки методов.

затем вы можете вернуться к исходному коду и вытащить часть этого функционала из процедуры(ов) и в объекты.


всех видов пахнет можно найти в этом коде. Если вы действительно хотите сделать хороший код из него, прочитайте главу рефакторинга Фаулера за главой и примените все полученные знания немедленно.

другой подход-просто не трогать его, "не ремонтировать рабочий двигатель".