Selasa, 21 Oktober 2025

Pertemuan 9 - Implementasi World of Zuul

Analisis Desain Kelas PBO (Pertemuan 9) - Studi Kasus: World of Zuul

Pemrograman Berbasis Objek (PBO)

Nama   : Hosea Felix Sanjaya
NRP     : 5025241177
Kelas  : B
Tahun : 2025


Pendahuluan

Dalam materi perkuliahan Pemrograman Berbasis Objek (PBO) pertemuan ke-9, kita membahas dua konsep fundamental dalam desain perangkat lunak yang berkualitas: Cohesion (Kohesi) dan Coupling (Kopling). Prinsip utamanya adalah untuk selalu mengusahakan "High Cohesion, Loose Coupling".

  • High Cohesion (Kohesi Tinggi): Menandakan bahwa sebuah kelas memiliki satu tanggung jawab yang jelas dan terfokus. Semua metode dan atribut di dalam kelas tersebut bekerja bersama untuk mencapai satu tujuan tunggal.
  • Loose Coupling (Kopling Longgar): Menandakan bahwa antar kelas memiliki tingkat ketergantungan yang rendah. Perubahan pada satu kelas (misalnya, perbaikan internal) sebisa mungkin tidak mengharuskan perubahan pada kelas lain yang menggunakannya.

Untuk memahami penerapan konsep ini, kita akan melakukan analisis terhadap "World of Zuul", sebuah studi kasus game petualangan berbasis teks sederhana yang sangat populer digunakan dalam pengajaran OOP.

Deskripsi Sistem (World of Zuul)

World of Zuul adalah sebuah game petualangan berbasis teks yang sangat sederhana. Pemain dapat "berjalan-jalan" di dalam sebuah lingkungan kampus fiksi dengan mengetikkan perintah teks sederhana.

Tujuan dari game ini (dari sisi akademis) adalah untuk mendemonstrasikan bagaimana sebuah program dapat dipecah menjadi beberapa kelas yang saling berinteraksi, masing-masing dengan tanggung jawabnya sendiri.

Alur Sistem (Game Flow)

  1. Game dimulai dan menampilkan pesan selamat datang serta lokasi awal pemain.
  2. Game mencetak pintu keluar (exits) yang tersedia dari lokasi saat ini.
  3. Game menunggu pemain mengetikkan perintah (misalnya, "go south" atau "help").
  4. Parser akan membaca input dan mengubahnya menjadi objek Command.
  5. Game akan memproses objek Command tersebut:
    • Jika perintah "go", pemain akan dipindahkan ke Room lain (jika ada pintu).
    • Jika perintah "help", game akan menampilkan daftar perintah yang valid.
    • Jika perintah "quit", game akan berakhir.
  6. Loop berlanjut kembali ke langkah 2 sampai pemain mengetik "quit".

Kode Program Lengkap

Sistem ini dibangun dari 5 kelas utama, ditambah 1 kelas `Main` untuk menjalankannya.

1. Kelas Game.java

Ini adalah kelas utama yang menginisialisasi semua bagian game, membuat ruangan, dan menjalankan game loop utama. Kelas ini juga yang memproses perintah dari pengguna.


/**
 * This class is the main class of the "World of Zuul" application. 
 * "World of Zuul" is a very simple, text based adventure game.  Users 
 * can walk around some scenery. That's all. It should really be extended 
 * to make it more interesting!
 *
 * To play this game, create an instance of this class and call the "play"
 * method.
 *
 * This main class creates and initialises all the others: it creates all
 * rooms, creates the parser and starts the game.  It also evaluates and
 * executes the commands that the parser returns.
 *
 * @author  Michael Kolling and David J. Barnes
 * @version 1.0 (February 2002)
 */

class Game 
{
    private Parser parser;
    private Room currentRoom;
        
    /**
     * Create the game and initialise its internal map.
     */
    public Game() 
    {
        createRooms();
        parser = new Parser();
    }

    /**
     * Create all the rooms and link their exits together.
     */
    private void createRooms()
    {
        Room outside, theatre, pub, lab, office;
      
        // create the rooms
        outside = new Room("outside the main entrance of the university");
        theatre = new Room("in a lecture theatre");
        pub = new Room("in the campus pub");
        lab = new Room("in a computing lab");
        office = new Room("in the computing admin office");
        
        // initialise room exits
        outside.setExits(null, theatre, lab, pub);
        theatre.setExits(null, null, null, outside);
        pub.setExits(null, outside, null, null);
        lab.setExits(outside, office, null, null);
        office.setExits(null, null, null, lab);

        currentRoom = outside;  // start game outside
    }

    /**
     * Main play routine.  Loops until end of play.
     */
    public void play() 
    {            
        printWelcome();

        // Enter the main command loop.  Here we repeatedly read commands and
        // execute them until the game is over.
                
        boolean finished = false;
        while (! finished) {
            Command command = parser.getCommand();
            finished = processCommand(command);
        }
        System.out.println("Thank you for playing.  Good bye.");
    }

    /**
     * Print out the opening message for the player.
     */
    private void printWelcome()
    {
        System.out.println();
        System.out.println("Welcome to Adventure!");
        System.out.println("Adventure is a new, incredibly boring adventure game.");
        System.out.println("Type 'help' if you need help.");
        System.out.println();
        System.out.println("You are " + currentRoom.getDescription());
        System.out.print("Exits: ");
        if(currentRoom.northExit != null)
            System.out.print("north ");
        if(currentRoom.eastExit != null)
            System.out.print("east ");
        if(currentRoom.southExit != null)
            System.out.print("south ");
        if(currentRoom.westExit != null)
            System.out.print("west ");
        System.out.println();
    }

    /**
     * Given a command, process (that is: execute) the command.
     * If this command ends the game, true is returned, otherwise false is
     * returned.
     */
    private boolean processCommand(Command command) 
    {
        boolean wantToQuit = false;

        if(command.isUnknown()) {
            System.out.println("I don't know what you mean...");
            return false;
        }

        String commandWord = command.getCommandWord();
        if (commandWord.equals("help"))
            printHelp();
        else if (commandWord.equals("go"))
            goRoom(command);
        else if (commandWord.equals("quit"))
            wantToQuit = quit(command);

        return wantToQuit;
    }

    // implementations of user commands:

    /**
     * Print out some help information.
     * Here we print some stupid, cryptic message and a list of the 
     * command words.
     */
    private void printHelp() 
    {
        System.out.println("You are lost. You are alone. You wander");
        System.out.println("around at the university.");
        System.out.println();
        System.out.println("Your command words are:");
        System.out.println("   go quit help");
    }

    /** * Try to go to one direction. If there is an exit, enter
     * the new room, otherwise print an error message.
     */
    private void goRoom(Command command) 
    {
        if(!command.hasSecondWord()) {
            // if there is no second word, we don't know where to go...
            System.out.println("Go where?");
            return;
        }

        String direction = command.getSecondWord();

        // Try to leave current room.
        Room nextRoom = null;
        if(direction.equals("north"))
            nextRoom = currentRoom.northExit;
        if(direction.equals("east"))
            nextRoom = currentRoom.eastExit;
        if(direction.equals("south"))
            nextRoom = currentRoom.southExit;
        if(direction.equals("west"))
            nextRoom = currentRoom.westExit;

        if (nextRoom == null)
            System.out.println("There is no door!");
        else {
            currentRoom = nextRoom;
            System.out.println("You are " + currentRoom.getDescription());
            System.out.print("Exits: ");
            if(currentRoom.northExit != null)
                System.out.print("north ");
            if(currentRoom.eastExit != null)
                System.out.print("east ");
            if(currentRoom.southExit != null)
                System.out.print("south ");
            if(currentRoom.westExit != null)
                System.out.print("west ");
            System.out.println();
        }
    }

    /** * "Quit" was entered. Check the rest of the command to see
     * whether we really quit the game. Return true, if this command
     * quits the game, false otherwise.
     */
    private boolean quit(Command command) 
    {
        if(command.hasSecondWord()) {
            System.out.println("Quit what?");
            return false;
        }
        else
            return true;  // signal that we want to quit
    }
}

2. Kelas Room.java

Kelas ini merepresentasikan satu lokasi atau ruangan dalam game. Setiap ruangan memiliki deskripsi dan referensi ke ruangan lain melalui pintu keluar (exit).


/**
 * Class Room - a room in an adventure game.
 *
 * This class is the main class of the "World of Zuul"
 * application. 
 * "World of Zuul" is a very simple, text based adventure game.
 *
 * A "Room" represents one location in the scenery of the game.  It is 
 * connected to other rooms via exits.  The exits are labelled north, 
 * east, south, west.  For each direction, the room stores a reference
 * to the neighboring room, or null if there is no exit
 * in that direction.
 *
 * @author  Michael Kolling and David J. Barnes
 * @version 1.0 (February 2002)
 */

class Room 
{
    public String description;
    public Room northExit;
    public Room southExit;
    public Room eastExit;
    public Room westExit;

    /**
     * Create a room described "description". Initially, it has
     * no exits. "description" is something like "a kitchen" or
     * "an open court yard".
     */
    public Room(String description) 
    {
        this.description = description;
    }

    /**
     * Define the exits of this room.  Every direction either leads
     * to another room or is null (no exit there).
     */
    public void setExits(Room north, Room east, Room south, Room west) 
    {
        if(north != null)
            northExit = north;
        if(east != null)
            eastExit = east;
        if(south != null)
            southExit = south;
        if(west != null)
            westExit = west;
    }

    /**
     * Return the description of the room (the one that was defined
     * in the constructor).
     */
    public String getDescription()
    {
        return description;
    }
}

3. Kelas Parser.java

Kelas ini bertanggung jawab membaca input dari pengguna dan mengubahnya menjadi objek Command yang dapat dimengerti oleh game.


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

/**
 * This class is the main class of the "World of Zuul"
 * application. 
 * "World of Zuul" is a very simple, text based adventure game.
 *
 * This parser reads user input and tries to interpret it as an "Adventure"
 * command. Every time it is called it reads a line from the terminal and
 * tries to interpret the line as a two word command. It returns the command
 * as an object of class Command.
 *
 * The parser has a set of known command words. It checks user input against
 * the known commands, and if the input is not one of the known commands, it
 * returns a command object that is marked as an unknown command.
 *
 * @author  Michael Kolling and David J. Barnes
 * @version 1.0 (February 2002)
 */

class Parser 
{

    private CommandWords commands;  // holds all valid command words

    public Parser() 
    {
        commands = new CommandWords();
    }

    public Command getCommand() 
    {
        String inputLine = "";   // will hold the full input line
        String word1;
        String word2;

        System.out.print("> ");     // print prompt

        BufferedReader reader = 
            new BufferedReader(new InputStreamReader(System.in));
        try {
            inputLine = reader.readLine();
        }
        catch(java.io.IOException exc) {
            System.out.println ("There was an error during reading: "
                                + exc.getMessage());
        }

        StringTokenizer tokenizer = new StringTokenizer(inputLine);

        if(tokenizer.hasMoreTokens())
            word1 = tokenizer.nextToken();      // get first word
        else
            word1 = null;
        if(tokenizer.hasMoreTokens())
            word2 = tokenizer.nextToken();      // get second word
        else
            word2 = null;

        // note: we just ignore the rest of the input line.

        // Now check whether this word is known. If so, create a command
        // with it. If not, create a "null" command (for unknown command).

        if(commands.isCommand(word1))
            return new Command(word1, word2);
        else
            return new Command(null, word2);
    }
}

4. Kelas Command.java

Kelas ini adalah kelas data sederhana yang menyimpan informasi tentang perintah yang diketik pengguna. Ini memisahkan kata perintah (kata pertama) dan kata kedua.


/**
 * This class is the main class of the "World of Zuul" application. 
 * "World of Zuul" is a very simple, text based adventure game.
 *
 * This class holds information about a command that was issued by the user.
 * A command currently consists of two strings: a command word and a second
 * word (for example, if the command was "take map", then the two strings
 * are "take" and "map").
 *
 * The way this is used is: Commands are already checked for being valid
 * command words. If the user entered an invalid command (a word that is not
 * known) then the command word is <null>.
 *
 * If the command has only one word, then the second word is <null>.
 *
 * @author  Michael Kolling and David J. Barnes
 * @version 1.0 (February 2002)
 */

class Command
{
    private String commandWord;
    private String secondWord;

    /**
     * Create a command object. First and second word must be supplied, but
     * either one (or both) can be null. The command word should be null to
     * indicate that this was a command that was not recognised by this game.
     */
    public Command(String firstWord, String secondWord)
    {
        commandWord = firstWord;
        this.secondWord = secondWord;
    }

    /**
     * Return the command word (the first word) of this command. If the
     * command was not understood, the result is null.
     */
    public String getCommandWord()
    {
        return commandWord;
    }

    /**
     * Return the second word of this command. Returns null if there was no
     * second word.
     */
    public String getSecondWord()
    {
        return secondWord;
    }

    /**
     * Return true if this command was not understood.
     */
    public boolean isUnknown()
    {
        return (commandWord == null);
    }

    /**
     * Return true if the command has a second word.
     */
    public boolean hasSecondWord()
    {
        return (secondWord != null);
    }
}

5. Kelas CommandWords.java

Kelas ini memiliki satu tanggung jawab: menyimpan dan memvalidasi daftar semua kata perintah yang sah dalam game.


/**
 * This class is the main class of the "World of Zuul"
 * application. 
 * "World of Zuul" is a very simple, text based
 * adventure game.
 *
 * This class holds an enumeration of all command words
 * known to the game.
 * It is used to recognise commands as they are typed
 * in.
 *
 * @author  Michael Kolling and David J. Barnes
 * @version 1.0 (February 2002)
 */

class CommandWords
{
    // a constant array that holds all valid command
    // words
    private static final String validCommands[] = {
        "go", "quit", "help", "look"
    };

    /**
     * Constructor - initialise the command words.
     */
    public CommandWords()
    {
        // nothing to do at the moment...
    }

    /**
     * Check whether a given String is a valid command
     * word. 
     * Return true if it is, false if it isn't.
     */
    public boolean isCommand(String aString)
    {
        for(int i = 0; i < validCommands.length; i++) {
            if(validCommands[i].equals(aString))
                return true;
        }
        // if we get here, the string was not found in
        // the commands
        return false;
    }
}

6. Kelas Main.java (Untuk Menjalankan)

Kelas ini tidak ada di gambar, namun diperlukan untuk membuat objek `Game` dan memulai permainan.


public class Main {
    public static void main(String[] args) {
        // Buat objek Game baru
        Game game = new Game();
        
        // Panggil metode play() untuk memulai permainan
        game.play();
    }
}

Analisis Desain (Kohesi & Kopling)

Sekarang, mari kita evaluasi desain kelas "World of Zuul" menggunakan konsep yang telah kita pelajari.

Analisis Kohesi (Prinsip: High Cohesion)

Kohesi mengukur seberapa fokus sebuah kelas terhadap satu tanggung jawab. Desain yang baik memiliki Kohesi Tinggi.

Contoh Kohesi Tinggi (Desain Baik)

  • Room: Sangat kohesif. Tanggung jawabnya hanya satu: merepresentasikan satu lokasi. Semua data (description, northExit, dll.) dan metode (setExits, getDescription) hanya berfokus pada properti ruangan itu saja.
  • Command: Sangat kohesif. Tanggung jawabnya hanya menyimpan dua kata dari perintah pengguna (commandWord dan secondWord).
  • CommandWords: Sangat kohesif. Tanggung jawab tunggalnya adalah mengetahui dan memvalidasi semua kata perintah yang sah dalam game.
  • Parser: Kohesif. Tanggung jawabnya jelas, yaitu membaca input mentah dari pengguna dan mengubahnya menjadi objek Command.

Contoh Kohesi Rendah (Desain Kurang Baik)

  • Game: Kelas ini adalah contoh utama dari Kohesi Rendah. Perhatikan semua tanggung jawabnya yang berbeda:
    1. Membuat dan menginisialisasi semua ruangan (createRooms).
    2. Menjalankan game loop utama (play).
    3. Memproses setiap jenis perintah (processCommand).
    4. Menjalankan logika spesifik untuk setiap perintah (printHelp, goRoom, quit).

Kelas Game melakukan terlalu banyak hal. Ini membuatnya sulit dipahami, di-debug, dan dirawat. Jika kita ingin menambah 10 perintah baru, kita harus mengubah kelas Game di banyak tempat.

Analisis Kopling (Prinsip: Loose Coupling)

Kopling mengukur seberapa besar ketergantungan satu kelas terhadap kelas lain. Desain yang baik memiliki Kopling Longgar (Loose Coupling).

Contoh Kopling Longgar (Desain Baik)

  • Parser dan Game: Kopling di antara keduanya cukup longgar.
    • Kelas Game hanya perlu memanggil parser.getCommand().
    • Game tidak peduli bagaimana Parser mendapatkan perintah itu (apakah dari terminal, file, atau internet).
    • Parser juga tidak tahu apa-apa tentang Game atau Room. Ia hanya mengembalikan objek Command.
    • Ini adalah desain yang bagus. Kita bisa mengganti seluruh implementasi Parser tanpa merusak kelas Game.

Contoh Kopling Erat (Desain Kurang Baik)

  • Game dan Command: Di dalam metode processCommand, terdapat blok if-else yang besar. Ini adalah kopling yang erat. Jika kita ingin menambah perintah baru (misal, "look" atau "take"), kita harus memodifikasi kode internal kelas Game dengan menambah else if baru. Ini membuat Game sangat bergantung pada setiap detail perintah yang ada.
  • Game dan Room: Ini adalah contoh terburuk dalam kode ini. Kelas Game (dalam goRoom dan printWelcome) mengakses fields dari Room secara langsung:
    nextRoom = currentRoom.northExit;
    if(currentRoom.northExit != null) ...
    Ini terjadi karena kelas Room mendeklarasikan variabelnya sebagai public (public Room northExit;), yang merupakan pelanggaran berat terhadap prinsip Enkapsulasi.

    Ini adalah kopling yang sangat erat. Game bergantung pada struktur internal Room. Jika kita ingin mengubah cara Room menyimpan exit (misalnya, menjadi HashMap), maka kelas Game akan rusak total dan harus ditulis ulang.

Kesimpulan

Studi kasus "World of Zuul" adalah contoh yang sangat baik untuk belajar. Desainnya berhasil memisahkan beberapa tanggung jawab dengan baik (seperti Parser dan Command) yang menunjukkan Kohesi Tinggi dan Kopling Longgar.

Namun, desain ini juga secara sengaja menunjukkan praktik yang buruk (Kohesi Rendah di kelas Game dan Kopling Erat antara Game dan Room) sebagai bahan pelajaran. Langkah selanjutnya dari materi ini biasanya adalah melakukan Refactoring: yaitu memperbaiki struktur kode ini untuk meningkatkan kohesi dan mengurangi kopling, tanpa mengubah fungsionalitas game.

Dokumentasi

Tidak ada komentar:

Posting Komentar

Pertemuan 15 : Aplikasi CRUD JAVA

Pertemuan 15 - Java CRUD Database Tanggal: 9 Desember 2025 Nama: Hosea Felix Sanjaya NRP: 5025241177 a. Setup & Persiapan Envi...