Object Oriented Programming | Week 12: Abstract Class
PBO-A 2025
Latihan 1: Abstraksi Makhluk hidup
Dalam pemrograman berorientasi objek, sebuah kelas abstrak adalah kelas yang tidak dapat dibuat objeknya secara langsung, melainkan berfungsi sebagai blueprint untuk kelas kelas turunannya. Kelas ini menetapkan struktur umum dan perilaku dasar yang harus diwarisi dan diimplementasikan oleh semua subkelas. Kelas abstrak dapat berisi method abstrak, yaitu method yang hanya dideklarasikan tanpa tubuh implementasi, sehingga memaksa subkelas konkret untuk menyediakan detail implementasi spesifik. Tujuan utamanya adalah untuk memastikan konsistensi dalam hirarki pewarisan dan untuk memfasilitasi polimorfisme dengan menyediakan antarmuka yang seragam. Contohnya dalam abstract class MakhlukHidup berikut, method berdiri() hanya dideklarasikan tanpa implementasi.
/**
* Write a description of class MakhlukHidup here.
*
* @author Muhammad Zaky Zein
* @version 2025.11.11
*/
public abstract class MakhlukHidup
{
public abstract void berdiri();
public void oksigen() {
System.out.println("- butuh makanan");
System.out.println("- butuh air");
System.out.println("- butuh oksigen");
}
}
/**
* Write a description of class Hewan here.
*
* @author Muhammad Zaky Zein
* @version 2025.11.11
*/
public class Hewan extends MakhlukHidup
{
private String kakiEmpat, kakiDua;
public Hewan(String kakiEmpat, String kakiDua) {
this.kakiEmpat = kakiEmpat;
this.kakiDua = kakiDua;
}
public void berdiri() {
System.out.println("Kambing berdiri menggunakan: " + kakiEmpat);
System.out.println("Ayam berdiri menggunakan: " + kakiDua);
}
}
/**
* Write a description of class Tumbuhan here.
*
* @author Muhammad Zaky Zein
* @version 2025.11.11
*/
public class Tumbuhan extends MakhlukHidup
{
private String akar;
public Tumbuhan(String akar) {
this.akar = akar;
}
public void berdiri() {
System.out.println("Tumbuhan berdiri menggunakan: " + akar);
}
}
/**
* Write a description of class Manusia here.
*
* @author Muhammad Zaky Zein
* @version 2025.11.11
*/
public class Manusia extends MakhlukHidup
{
private String duaKaki;
public Manusia(String duaKaki) {
this.duaKaki = duaKaki;
}
public void berdiri() {
System.out.println("Manusia berdiri menggunakan: " + duaKaki);
}
}/** * Write a description of abstract class Animal here. * * @author Muhammad Zaky Zein * @version 2025.11.11 */ import java.util.List; public abstract class Animal { private int age; private boolean alive; private Location location; public Animal() { age = 0; alive = true; } abstract public void act(Field currentField, Field updatedField, List<Animal> newAnimals); public boolean isAlive() { return alive; } public void setDead() { alive = false; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Location getLocation() { return location; } public void setLocation(int row, int col) { this.location = new Location(row, col); } public void setLocation(Location location) { this.location = location; } }
/** * Write a description of class Rabbit here. * * @author Muhammad Zaky Zein * @version 2025.11.11 */ import java.util.List; import java.util.Random; public class Rabbit extends Animal { private static final int BREEDING_AGE = 5; private static final int MAX_AGE = 50; private static final double BREEDING_PROBABILITY = 0.15; private static final int MAX_LITTER_SIZE = 5; private static final Random rand = new Random(); public Rabbit(boolean randomAge) { super(); if(randomAge) setAge(rand.nextInt(MAX_AGE)); } public void act(Field currentField, Field updatedField, List<Animal> newAnimals) { incrementAge(); if(isAlive()) { int births = breed(); for(int b = 0; b < births; b++) { Rabbit newRabbit = new Rabbit(false); newAnimals.add(newRabbit); newRabbit.setLocation( updatedField.randomAdjacentLocation(getLocation())); updatedField.place(newRabbit); } Location newLocation = updatedField.freeAdjacentLocation(getLocation()); if(newLocation != null) { setLocation(newLocation); updatedField.place(this); } else setDead(); } } private void incrementAge() { setAge(getAge() + 1); if(getAge() > MAX_AGE) setDead(); } private int breed() { int births = 0; if(canBreed() && rand.nextDouble() <= BREEDING_PROBABILITY) births = rand.nextInt(MAX_LITTER_SIZE) + 1; return births; } public String toString() { return "Rabbit, age " + getAge(); } private boolean canBreed() { return getAge() >= BREEDING_AGE; } }
/** * Write a description of class Fox here. * * @author Muhammad Zaky Zein * @version 2025.11.11 */ import java.util.List; import java.util.Iterator; import java.util.Random; public class Fox extends Animal { private static final int BREEDING_AGE = 10; private static final int MAX_AGE = 150; private static final double BREEDING_PROBABILITY = 0.09; private static final int MAX_LITTER_SIZE = 3; private static final int RABBIT_FOOD_VALUE = 4; private static final Random rand = new Random(); private int foodLevel; public Fox(boolean randomAge) { super(); if(randomAge) { setAge(rand.nextInt(MAX_AGE)); foodLevel = rand.nextInt(RABBIT_FOOD_VALUE); } else foodLevel = RABBIT_FOOD_VALUE; } public void act(Field currentField, Field updatedField, List<Animal> newAnimals) { incrementAge(); incrementHunger(); if(isAlive()) { int births = breed(); for(int b = 0; b < births; b++) { Fox newFox = new Fox(false); newAnimals.add(newFox); newFox.setLocation( updatedField.randomAdjacentLocation(getLocation())); updatedField.place(newFox); } Location newLocation = findFood(currentField, getLocation()); if(newLocation == null) newLocation = updatedField.freeAdjacentLocation(getLocation()); if(newLocation != null) { setLocation(newLocation); updatedField.place(this); } else setDead(); } } private void incrementAge() { setAge(getAge() + 1); if(getAge() > MAX_AGE) setDead(); } private void incrementHunger() { foodLevel--; if(foodLevel <= 0) setDead(); } private Location findFood(Field field, Location location) { Iterator<Location> adjacentLocations = field.adjacentLocations(location); while(adjacentLocations.hasNext()) { Location where = (Location) adjacentLocations.next(); Object animal = field.getObjectAt(where); if(animal instanceof Rabbit) { Rabbit rabbit = (Rabbit) animal; if(rabbit.isAlive()) { rabbit.setDead(); foodLevel = RABBIT_FOOD_VALUE; return where; } } } return null; } private int breed() { int births = 0; if(canBreed() && rand.nextDouble() <= BREEDING_PROBABILITY) births = rand.nextInt(MAX_LITTER_SIZE) + 1; return births; } public String toString() { return "Fox, age " + getAge(); } private boolean canBreed() { return getAge() >= BREEDING_AGE; } }
/** * Write a description of class Field here. * * @author Muhammad Zaky Zein * @version 2025.11.11 */ import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.Random; public class Field { private static final Random rand = new Random(); private int depth, width; private Animal[][] field; public Field(int depth, int width) { this.depth = depth; this.width = width; field = new Animal[depth][width]; } public void clear() { for(int row = 0; row < depth; row++) { for(int col = 0; col < width; col++) { field[row][col] = null; } } } public void place(Animal animal) { Location location = animal.getLocation(); field[location.getRow()][location.getCol()] = animal; } public Animal getObjectAt(Location location) { return getObjectAt(location.getRow(), location.getCol()); } public Animal getObjectAt(int row, int col) { return field[row][col]; } public Location randomAdjacentLocation(Location location) { int row = location.getRow(); int col = location.getCol(); int nextRow = row + rand.nextInt(3) - 1; int nextCol = col + rand.nextInt(3) - 1; if(nextRow < 0 || nextRow >= depth || nextCol < 0 || nextCol >= width) return location; else if(nextRow != row || nextCol != col) return new Location(nextRow, nextCol); else return location; } public Location freeAdjacentLocation(Location location) { Iterator<Location> adjacent = adjacentLocations(location); while(adjacent.hasNext()) { Location next = (Location) adjacent.next(); if(field[next.getRow()][next.getCol()] == null) return next; } if(field[location.getRow()][location.getCol()] == null) return location; else return null; } public Iterator<Location> adjacentLocations(Location location) { int row = location.getRow(); int col = location.getCol(); LinkedList<Location> locations = new LinkedList<>(); for(int roffset = -1; roffset <= 1; roffset++) { int nextRow = row + roffset; if(nextRow >= 0 && nextRow < depth) { for(int coffset = -1; coffset <= 1; coffset++) { int nextCol = col + coffset; if(nextCol >= 0 && nextCol < width && (roffset != 0 || coffset != 0)) locations.add(new Location(nextRow, nextCol)); } } } Collections.shuffle(locations,rand); return locations.iterator(); } public int getDepth() { return depth; } public int getWidth() { return width; } }
/** * Write a description of class FieldStats here. * * @author Muhammad Zaky Zein * @version 2025.11.11 */ import java.util.HashMap; import java.util.Iterator; public class FieldStats { private HashMap<Class<?>, Counter> counters; private boolean countsValid; public FieldStats() { counters = new HashMap<>(); countsValid = true; } public String getPopulationDetails(Field field) { StringBuffer buffer = new StringBuffer(); if(!countsValid) generateCounts(field); Iterator<Class<?>> keys = counters.keySet().iterator(); while(keys.hasNext()) { Counter info = (Counter) counters.get(keys.next()); buffer.append(info.getName()); buffer.append(": "); buffer.append(info.getCount()); buffer.append(' '); } return buffer.toString(); } public void reset() { countsValid = false; Iterator<Class<?>> keys = counters.keySet().iterator(); while(keys.hasNext()) { Counter cnt = (Counter) counters.get(keys.next()); cnt.reset(); } } public void incrementCount(Class<?> animalClass) { Counter cnt = (Counter) counters.get(animalClass); if(cnt == null) { cnt = new Counter(animalClass.getName()); counters.put(animalClass, cnt); } cnt.increment(); } public void countFinished() { countsValid = true; } public boolean isViable(Field field) { int nonZero = 0; if(!countsValid) generateCounts(field); Iterator<Class<?>> keys = counters.keySet().iterator(); while(keys.hasNext()) { Counter info = (Counter) counters.get(keys.next()); if(info.getCount() > 0) nonZero++; } return nonZero > 1; } private void generateCounts(Field field) { reset(); for(int row = 0; row < field.getDepth(); row++) { for(int col = 0; col < field.getWidth(); col++) { Object animal = field.getObjectAt(row, col); if(animal != null) incrementCount(animal.getClass()); } } countsValid = true; } }
/** * Write a description of class Counter here. * * @author Muhammad Zaky Zein * @version 2025.11.11 */ public class Counter { private String name; private int count; public Counter(String name) { this.name = name; count = 0; } public String getName() { return name; } public int getCount() { return count; } public void increment() { count++; } public void reset() { count = 0; } }
/** * Write a description of class Location here. * * @author Muhammad Zaky Zein * @version 2025.11.11 */ public class Location { private int row; private int col; public Location(int row, int col) { this.row = row; this.col = col; } public boolean equals(Object obj) { if(obj instanceof Location) { Location other = (Location) obj; return row == other.getRow() && col == other.getCol(); } else return false; } public String toString() { return row + "," + col; } public int hashCode() { return (row << 16) + col; } public int getRow() { return row; } public int getCol() { return col; } }
/** * Write a description of class PopulationGenerator here. * * @author Muhammad Zaky Zein * @version 2025.11.11 */ import java.util.Random; import java.util.List; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.awt.Color; public class PopulationGenerator { private static final double FOX_CREATION_PROBABILITY = 0.02; private static final double RABBIT_CREATION_PROBABILITY = 0.08; private Map<Class<?>, Color> colorMap; private Color FOX_COLOR = Color.BLUE; private Color RABBIT_COLOR = Color.ORANGE; public PopulationGenerator() { colorMap = new HashMap<>(); colorMap.put(Fox.class, FOX_COLOR); colorMap.put(Rabbit.class, RABBIT_COLOR); } public void populate(Field field, List<Animal> animals) { Random rand = new Random(); field.clear(); for(int row = 0; row < field.getDepth(); row++) { for(int col = 0; col < field.getWidth(); col++) { if(rand.nextDouble() <= FOX_CREATION_PROBABILITY) { Fox fox = new Fox(true); fox.setLocation(row, col); animals.add(fox); field.place(fox); } else if(rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) { Rabbit rabbit = new Rabbit(true); rabbit.setLocation(row, col); animals.add(rabbit); field.place(rabbit); } } } Collections.shuffle(animals); } public Map<Class<?>, Color> getColors() { return colorMap; } }
/** * Write a description of class SimulatorView here. * * @author Muhammad Zaky Zein * @version 2025.11.11 */ import java.awt.*; import javax.swing.*; import java.util.HashMap; import java.util.Map; public class SimulatorView extends JFrame { private static final Color EMPTY_COLOR = Color.white; private static final Color UNKNOWN_COLOR = Color.gray; private final String STEP_PREFIX = "Step: "; private final String POPULATION_PREFIX = "Population: "; private JLabel stepLabel, population; private FieldView fieldView; private HashMap<Class<?>, Color> colors; private FieldStats stats; public SimulatorView(int height, int width) { stats = new FieldStats(); colors = new HashMap<>(); setTitle("Fox and Rabbit Simulation"); stepLabel = new JLabel(STEP_PREFIX, JLabel.CENTER); population = new JLabel(POPULATION_PREFIX, JLabel.CENTER); setLocation(100, 50); fieldView = new FieldView(height, width); Container contents = getContentPane(); contents.add(stepLabel, BorderLayout.NORTH); contents.add(fieldView, BorderLayout.CENTER); contents.add(population, BorderLayout.SOUTH); pack(); setVisible(true); } public void setColor(Class<?> animalClass, Color color) { colors.put(animalClass, color); } public void setColors(Map<Class<?>, Color> colorMap) { colors.putAll(colorMap); } private Color getColor(Class<?> animalClass) { Color col = (Color)colors.get(animalClass); if(col == null) return UNKNOWN_COLOR; else return col; } public void showStatus(int step, Field field) { if(!isVisible()) setVisible(true); stepLabel.setText(STEP_PREFIX + step); stats.reset(); fieldView.preparePaint(); for(int row = 0; row < field.getDepth(); row++) { for(int col = 0; col < field.getWidth(); col++) { Object animal = field.getObjectAt(row, col); if(animal != null) { stats.incrementCount(animal.getClass()); fieldView.drawMark(col, row, getColor(animal.getClass())); } else fieldView.drawMark(col, row, EMPTY_COLOR); } } stats.countFinished(); population.setText(POPULATION_PREFIX + stats.getPopulationDetails(field)); fieldView.repaint(); } public boolean isViable(Field field) { return stats.isViable(field); } private class FieldView extends JPanel { private final int GRID_VIEW_SCALING_FACTOR = 6; private int gridWidth, gridHeight; private int xScale, yScale; Dimension size; private Graphics g; private Image fieldImage; public FieldView(int height, int width) { gridHeight = height; gridWidth = width; size = new Dimension(0, 0); } public Dimension getPreferredSize() { return new Dimension(gridWidth * GRID_VIEW_SCALING_FACTOR, gridHeight * GRID_VIEW_SCALING_FACTOR); } public void preparePaint() { if(! size.equals(getSize())) { size = getSize(); fieldImage = fieldView.createImage(size.width, size.height); g = fieldImage.getGraphics(); xScale = size.width / gridWidth; if(xScale < 1) xScale = GRID_VIEW_SCALING_FACTOR; yScale = size.height / gridHeight; if(yScale < 1) yScale = GRID_VIEW_SCALING_FACTOR; } } public void drawMark(int x, int y, Color color) { g.setColor(color); g.fillRect(x * xScale, y * yScale, xScale-1, yScale-1); } public void paintComponent(Graphics g) { if(fieldImage != null) { Dimension currentSize = getSize(); if(size.equals(currentSize)) g.drawImage(fieldImage, 0, 0, null); else g.drawImage(fieldImage, 0, 0, currentSize.width, currentSize.height, null); } } } }
/**
* Write a description of class Simulator here.
*
* @author Muhammad Zaky Zein
* @version 2025.11.11
*/
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
public class Simulator
{
private static final int DEFAULT_WIDTH = 50;
private static final int DEFAULT_DEPTH = 50;
private List<Animal> animals;
private List<Animal> newAnimals;
private Field field;
private Field updatedField;
private int step;
private SimulatorView view;
private PopulationGenerator populationGenerator;
public Simulator() {
this(DEFAULT_DEPTH, DEFAULT_WIDTH);
}
public Simulator(int depth, int width) {
if(width <= 0 || depth <= 0) {
System.out.println("The dimensions must be greater than zero.");
System.out.println("Using default values.");
depth = DEFAULT_DEPTH;
width = DEFAULT_WIDTH;
}
animals = new ArrayList<>();
newAnimals = new ArrayList<>();
field = new Field(depth, width);
updatedField = new Field(depth, width);
view = new SimulatorView(depth, width);
populationGenerator = new PopulationGenerator();
view.setColors(populationGenerator.getColors());
reset();
}
public void runLongSimulation() { simulate(500); }
public void simulate(int numSteps) {
for(int step = 1; step <= numSteps && view.isViable(field); step++) {
simulateOneStep();
}
}
public void simulateOneStep() {
step++;
newAnimals.clear();
for(Iterator<Animal> iter = animals.iterator(); iter.hasNext(); ) {
Animal animal = (Animal)iter.next();
animal.act(field, updatedField, newAnimals);
}
animals.addAll(newAnimals);
Field temp = field;
field = updatedField;
updatedField = temp;
updatedField.clear();
view.showStatus(step, field);
}
public void reset() {
step = 0;
animals.clear();
field.clear();
updatedField.clear();
populationGenerator.populate(field, animals);
view.showStatus(step, field);
}
}
Comments
Post a Comment