DESIGNING CLASSES THE GAME OF LIFE

Slides:



Advertisements
Similar presentations
1 CSC 221: Computer Programming I Fall 2006 interacting objects modular design: dot races constants, static fields cascading if-else, logical operators.
Advertisements

Comp1004: Building Better Classes II Software Design Partly based on BlueJ Book – Chapter 7.
Lesson-06 Designing classes
Designing classes How to write classes in a way that they are easily understandable, maintainable and reusable 4.0.
Objects First With Java A Practical Introduction Using BlueJ Designing object-oriented programs How to write code in a way that is easily understandable,
VBA Modules, Functions, Variables, and Constants
Designing Classes How to write classes in a way that they are easily understandable, maintainable and reusable.
Well-behaved objects Improving your coding skills 1.0.
Design: Coupling and Cohesion How to write classes in a way that they are easily understandable, maintainable and reusable.
Make Sure You Know All This!. Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling 2 Objects and Classes.
STREAM STREAM A software process The Mañana Principle The Mañana Principle Don’t try to everything at once! Static Methods Static Methods Stateless Utility.
ARRAYS EXAMPLE: THE GAME OF LIFE CITS Scope of this lecture The Game of Life Implementation Performance Issues References:
Chapter 7 Designing Classes. Class Design When we are developing a piece of software, we want to design the software We don’t want to just sit down and.
REFACTORING Lecture 4. Definition Refactoring is a process of changing the internal structure of the program, not affecting its external behavior and.
Designing classes How to write classes in a way that they are easily understandable, maintainable and reusable 5.0.
© The McGraw-Hill Companies, 2006 Chapter 4 Implementing methods.
Programming in Java Unit 2. Class and variable declaration A class is best thought of as a template from which objects are created. You can create many.
1 CSC 221: Computer Programming I Spring 2010 interaction & design  modular design: roulette game  constants, static fields  % operator, string equals.
Designing classes How to write classes in a way that they are easily understandable, maintainable and reusable 3.0.
CMPS 211 JavaScript Topic 1 JavaScript Syntax. 2Outline Goals and Objectives Goals and Objectives Chapter Headlines Chapter Headlines Introduction Introduction.
Copyright © 2002, Systems and Computer Engineering, Carleton University a-JavaReview.ppt * Object-Oriented Software Development Unit.
Java Threads. What is a Thread? A thread can be loosely defined as a separate stream of execution that takes place simultaneously with and independently.
Programming Principles Chapter 1. Objectives Discuss the program design process. Introduce the Game of Life. Discuss object oriented design. – Information.
Designing Classes 2 How to write classes in a way that they are easily understandable, maintainable and reusable.
An Object-Oriented Approach to Programming Logic and Design Fourth Edition Chapter 6 Using Methods.
Designing classes How to write classes in a way that they are easily understandable, maintainable and reusable 5.0.
Topic 1 Object Oriented Programming. 1-2 Objectives To review the concepts and terminology of object-oriented programming To discuss some features of.
Chapter 6 Introduction to Defining Classes. Objectives: Design and implement a simple class from user requirements. Organize a program in terms of a view.
Summing Up Object Oriented Design. Four Major Components: Abstraction modeling real-life entities by essential information only Encapsulation clustering.
Programming with Java © 2002 The McGraw-Hill Companies, Inc. All rights reserved. 1 McGraw-Hill/Irwin Chapter 5 Creating Classes.
Designing Classes. Software changes Software is not like a novel that is written once and then remains unchanged. Software is extended, corrected, maintained,
Final Review. From ArrayLists to Arrays The ArrayList : used to organize a list of objects –It is a class in the Java API –the ArrayList class uses an.
M1G Introduction to Programming 2 3. Creating Classes: Room and Item.
1 COS 260 DAY 14 Tony Gauvin. 2 Agenda Questions? 6 th Mini quiz graded  Oct 29 –Chapter 6 Assignment 4 will be posted later Today –First two problems.
Objects First With Java A Practical Introduction Using BlueJ Designing classes How to write classes in a way that they are easily understandable, maintainable.
1 COS 260 DAY 12 Tony Gauvin. 2 Agenda Questions? 5 th Mini quiz –Chapter 5 40 min Assignment 3 Due Assignment 4 will be posted later (next week) –If.
Chapter 4: More Object Concepts. Objectives Understand blocks and scope Overload a method Avoid ambiguity Create and call constructors with parameters.
CONDITIONALS CITS1001. Scope of this lecture if statements switch statements Source ppts: Objects First with Java - A Practical Introduction using BlueJ,
Designing classes How to write classes in a way that they are easily understandable, maintainable and reusable 6.0.
Chapter 5 Introduction to Defining Classes Fundamentals of Java.
Review. Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Objects and Classes Objects and Classes –State.
Arrays Chapter 7.
Objektorienterad programmering d2, förel. 8
Visit for more Learning Resources
User-Written Functions
Learning to Program D is for Digital.
Data Abstraction: The Walls
Chapter 3: Using Methods, Classes, and Objects
Java Programming: Guided Learning with Early Objects
Objects First with Java
ARRAYS example: THE GAME OF LIFE
Phil Tayco Slide version 1.0 Created Oct 2, 2017
Objects First with Java
File Handling Programming Guides.
slides created by Ethan Apter
6 Chapter Functions.
Java Programming Arrays
Fundamental Error Handling
Programming Logic and Design Fourth Edition, Comprehensive
slides created by Ethan Apter
Topics Introduction to Value-returning Functions: Generating Random Numbers Writing Your Own Value-Returning Functions The math Module Storing Functions.
Objektorienterad programmering d2, förel. 8
COS 260 DAY 14 Tony Gauvin.
Tonga Institute of Higher Education IT 141: Information Systems
Based on slides created by Bjarne Stroustrup & Tony Gaddis
Tonga Institute of Higher Education IT 141: Information Systems
Further abstraction techniques
Chap 2. Identifiers, Keywords, and Types
slides created by Ethan Apter and Marty Stepp
CMSC 202 Constructors Version 9/10.
Presentation transcript:

DESIGNING CLASSES THE GAME OF LIFE CITS1001

Designing Classes How can we write classes in a way that they are easily understandable, maintainable and reusable ?

Scope of this lecture Responsibility-driven design Coupling Cohesion Refactoring Case Study: The Game of Life Implementation Performance Issues Reading & Further Projects: Objects First (text book) Chapter 8 + 7 (6th ed.) or Chapter 6 (5th ed.) Game of Life References: http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life http://www.bitstorm.org/gameoflife/ (and hundreds of other websites)

Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Software changes Software is not like a novel that is written once and then remains unchanged. Software is extended, corrected, maintained, ported, adapted, … The work is done by different people over time (often decades).

Change or die There are only two options for software: Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Change or die There are only two options for software: Either it is continuously maintained or it dies. Software that cannot be maintained will be thrown away.

Code and design quality Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Code and design quality If we are to be critical of code quality, we need evaluation criteria. Two important concepts for assessing the quality of code are: Coupling Cohesion

Coupling Coupling refers to links between separate units of a program. Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Coupling Coupling refers to links between separate units of a program. If two classes depend closely on many details of each other, we say they are tightly coupled. We aim for loose coupling. A class diagram provides (limited) hints at the degree of coupling.

Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Cohesion Cohesion refers to the number and diversity of tasks that a single unit is responsible for. If each unit is responsible for one single logical task, we say it has high cohesion. We aim for high cohesion. ‘Unit’ applies to classes, methods and modules (packages).

Game of Life Invented by John Conway over forty years ago An example of a “cellular automaton” The game is played on a rectangular grid of “cells”

A model world This grid is a “model world”, where each cell is either occupied or vacant The initial configuration can be specified by the user, or chosen randomly We colour the occupied cells, and leave the others blank; the colour scheme is irrelevant to the game, but makes the application look more attractive

Births and Deaths In this model world, time passes in discrete steps known as generations The beginning of time is Generation 0 At each time step, the occupants of the model world live or die according to the following rules: any individual with zero or one neighbours dies of loneliness any individual with four or more neighbours dies of overcrowding a new individual is born in any unoccupied cell with exactly three neighbours The neighbours of a cell are the occupants of the eight cells surrounding that particular cell

Example Initial configuration Next generation 1 2 3 2 1 1 1 2 1 1 1 2 3 2 1 Number of neighbours of each cell Initial configuration Next generation

Interpretation The occupants of these cells died of loneliness 1 2 3 2 1 These two are new-born “babies” 1 1 2 1 1 1 2 3 2 1

At the edges The edges of the world are assumed to “wrap around”, so the map is actually a flat map of a torus-shaped world Neighbourhoods

Program Design The program needs classes to perform the following operations Maintain and update the map of the world Display and update the screen view of the world We will design two classes for these two aspects, and use a SimpleCanvas to provide the display Therefore we will create two classes Life LifeViewer

Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Coupling (reprise) Coupling refers to links between separate units of a program. If two classes depend closely on many details of each other, we say they are tightly coupled. We aim for loose coupling. A class diagram provides (limited) hints at the degree of coupling.

Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Coupling (reprise) Coupling refers to links between separate units of a program. If two classes depend closely on many details of each other, we say they are tightly coupled. We aim for loose coupling. A class diagram provides (limited) hints at the degree of coupling.

Loose coupling We aim for loose coupling. Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Loose coupling We aim for loose coupling. Loose coupling makes it possible to: understand one class without reading others; change one class with little or no effect on other classes. Thus: loose coupling increases maintainability.

Tight coupling We try to avoid tight coupling. Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Tight coupling We try to avoid tight coupling. Changes to one class bring a cascade of changes to other classes. Classes are harder to understand in isolation. Flow of control between objects of different classes is complex.

Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Cohesion (reprise) Cohesion refers to the number and diversity of tasks that a single unit is responsible for. If each unit is responsible for one single logical task, we say it has high cohesion. We aim for high cohesion. ‘Unit’ applies to classes, methods and modules (packages).

High cohesion We aim for high cohesion. Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling High cohesion We aim for high cohesion. High cohesion makes it easier to: understand what a class or method does; use descriptive names for variables, methods and classes; reuse classes and methods.

Loose (or low) cohesion Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Loose (or low) cohesion We aim to avoid loosely cohesive classes and methods. Methods perform multiple tasks. Classes have no clear identity.

Cohesion applied at different levels Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Cohesion applied at different levels Class level: Classes should represent one single, well defined entity. Method level: A method should be responsible for one and only one well defined task.

Code duplication Code duplication is an indicator of bad design, Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Code duplication Code duplication is an indicator of bad design, makes maintenance harder, can lead to introduction of errors during maintenance.

Game of Life Example: Separate the responsibilities The Life class should Maintain the current map of the world Update to the “next generation” when requested Provide access to the map for client use The LifeViewer class should Create a SimpleCanvas to display the map Display the initial generation Animate a client-specified number of generations

The Life instance variables public class Life { private boolean[][] map; private int width, height; // constructors & methods } We use a 2D array of type boolean for the map of the world; the value of the [i][j] entry in the array map indicates whether the (i,j) cell is populated or not

The first constructor public Life(boolean[][] initial) { map = initial; width = map.length; height = map[0].length; } This constructor allows the client to create a Life object with any initial pattern of occupied and unoccupied cells that they want

The second constructor public Life(int width, int height, double probability) { map = new boolean[width][height]; this.width = width; this.height = height; initializeMap(probability); } This constructor allows the client to specify just the width and height and have a random starting configuration created

Private helper method for initializing private void initializeMap(double probability) { for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) map[i][j] = Math.random() < probability); } If the user specifies 0.2 as the probability, approximately 20% of the cells will be occupied Math.random() is a quick way to get random doubles between 0 and 1

The default constructor It is polite to also give a “default” constructor for users who just wish to get going quickly, and are willing to accept programmer-chosen values public Life() { this(128, 128, 0.1); } Calls the 3-argument constructor with 128, 128, and 0.1 as the values

A new generation public void nextGeneration() { boolean[][] nextMap = new boolean[width][height]; for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) switch (numNeighbours(i, j)) { case 2: nextMap[i][j] = map[i][j]; break; case 3: nextMap[i][j] = true; break; default: nextMap[i][j] = false; } map = nextMap;

Code Dissection boolean[][] nextMap = new boolean[width][height]; This creates the space for the “next generation” to be stored in; we use the instance variables width and height to set the size We cannot do the updating “in place” because the rules specify that the updating from generation to generation occurs over the entire grid simultaneously; we cannot change some values and then use the new values in the calculation for other cells

Code dissection We haven’t defined this method yet switch (numNeighbours(i, j)) { case 2: nextMap[i][j] = map[i][j]; break; case 3: nextMap[i][j] = true; break; default: nextMap[i][j] = false; } The main loop processes each cell (i,j) in turn. The number of neighbours is calculated and the (i,j) cell of nextMap is set according to the rules of the Game of Life

Code dissection map = nextMap; The final statement makes the instance variable map refer to the newly completed “next generation” nextMap. Now everything is correctly updated.

Counting the neighbours private int numNeighbours(int i, int j) { int n=0; int ip = (i + 1) % width; int im = (width + i - 1) % width; int jp = (j + 1) % height; int jm = (height + j - 1) % height; if (map[im][jm]) n++; if (map[im][j]) n++; if (map[im][jp]) n++; if (map[i][jm]) n++; if (map[i][jp]) n++; if (map[ip][jm]) n++; if (map[ip][j]) n++; if (map[ip][jp]) n++; return n; }

Code Dissection [i-1][j-1] [i][j-1] [i+1][j-1] int ip = (i+1) % width; int im = (width+i-1) % width; int jp = (j+1) % height; int jm = (height+j-1) % height; [i-1][j] [i][j] [i+1][j] [i-1][j+1] [i][j+1] [i+1][j+1] Here, ip, im, jp, and jm refer to i+1, i-1, j+1 and j-1 respectively

Dealing with wrap-around When i is equal to width-1, then i+1 is equal to width, which is off the right-hand side of the picture. Calculating (i+1)%width has the effect of “wrapping around” back to 0, which is what is needed. Similarly when i is equal to 0, then (width+i-1)%width “wraps around” back to width-1. (i,j)

More dissection if (map[im][jm]) n++; if (map[im][j]) n++; if (map[im][jp]) n++; if (map[i][jm]) n++; if (map[i][jp]) n++; if (map[ip][jm]) n++; if (map[ip][j]) n++; if (map[ip][jp]) n++; return n; This is simply eight if statements that increment the variable n if the corresponding cell is occupied. Finally n is returned. Notice that the method is private, and only to be used by the Life object itself.

One performance issue As written, this code consumes a large amount of memory map nextMap

What happens during the update? After the statement map = nextMap map nextMap

When the method finishes The variable nextMap disappears map This area of memory is now “garbage”

Garbage Collection Java will automatically “collect” the garbage for you and recycle the space, but there is a performance penalty associated with this If the world is large and you are simulating many generations, the memory will rapidly fill up and the garbage collector will interrupt the smooth “animation” To fix this, it is better to keep two “maps of the world” as instance variables and just swap them over at each generation private boolean[][] map, nextMap;

In the nextGeneration() method Delete the code boolean[][] nextMap = new boolean[width][height]; because we do not want to create a new array every generation, but instead re-use the instance variable nextMap Replace map = nextMap; with boolean[][] swap = map; nextMap = swap;

How does this work? map nextMap swap swap = map;

Contd. map nextMap swap map = nextMap;

Contd. map nextMap swap nextMap = swap;

When the method finishes map nextMap swap disappears

Program Design Recall that our program design called for three classes Life LifeViewer SimpleCanvas The internal structure of the Life class has been written Now we must decide How the LifeViewer interacts with the Life object The internal structure of the LifeViewer object

Interaction The LifeViewer needs the Life object to do the following: Send the data for the current generation to be displayed Update to the next generation when required This means that the Life class must have the following methods public boolean[][] getMap() When called, this will return the array representing the current map public void nextGeneration() When called, this will update the Life object to the next generation

The extra method for Life public boolean[][] getMap() { return map; } Very simple method; simply returns a reference to the array representing the current generation

Variables defined in LifeViewer public class LifeViewer { private Life life; private int width, height; private SimpleCanvas c; private final static int CELL_SIZE = 4; private final static Color BACK_COLOUR = Color.white; private final static Color CELL_COLOUR = Color.red; private final static Color GRID_COLOUR = Color.black;

Instance Variables for LifeViewer public class LifeViewer { private Life life; private int width, height; private SimpleCanvas c; Each LifeViewer is responsible for displaying one Life object The width and height are stored as instance variables for convenience Each LifeViewer has one SimpleCanvas on which to draw

Class Variables for LifeViewer private final static int CELL_SIZE = 4; private final static Color BACK_COLOUR = Color.white; private final static Color CELL_COLOUR = Color.red; private final static Color GRID_COLOUR = Color.black; Here we define useful constants for the size of the cells and the colours to be used for the drawing. They are declared final because they will not change during one run of the program.

The constructor for LifeViewer public LifeViewer(Life life) { this.life = life; width = life.getMap().length; height = life.getMap()[0].length; c = new SimpleCanvas("Life", width * CELL_SIZE + 1, height * CELL_SIZE + 1, BACK_COLOUR); display(); }

What the constructor does The user must pass the LifeViewer an instance of Life to be displayed public LifeViewer(Life life) { this.life = life; width = life.getMap().length; height = life.getMap()[0].length; The constructor’s job is to initialize all the instance variables; the argument is an instance of the class Life, and so this must be saved in the corresponding instance variable. The width and height variables are then determined by asking the life object for a reference to its 2-d boolean array, and finding its dimensions

The constructor for LifeViewer c = new SimpleCanvas("Life", width * CELL_SIZE + 1, height * CELL_SIZE + 1, BACK_COLOUR); We need the “+1” to give the right hand edge and the bottom edge a nice border CELL_SIZE CELL_SIZE CELL_SIZE +1

Displaying the Life object Now we need a method to display the Life object This is basically three steps get the current map from the Life object draw the appropriate picture on the drawing area, which will involve painting every cell of the grid the right colour call c.repaint() to have the drawing rendered to the screen (It would be better to re-draw only the cells which have changed in the latest generation – how would you do that?)

A method to display the Life object private void display() { drawCells(); drawGrid(); c.repaint(); } Notice how the displaying functions – drawing the cells and drawing the grid – are all migrated to separate methods; this makes for code that is easy to read and to update

Drawing the cells private void drawCells() { Color col; for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) { if (life.getMap()[i][j]) col = CELL_COLUR; else col = BACK_COLOUR; c.setForegroundColour(col); c.drawRectangle(i * CELL_SIZE, j * CELL_SIZE, (i+1) * CELL_SIZE, (j+1) * CELL_SIZE); } For each cell in the array, set the colour and draw a rectangle of that colour at the appropriate coordinates

Drawing the grid Draw the vertical grid lines (constant x-values, full height) private void drawGrid() { c.setForegroundColour(GRID_COLOUR); for (int i = 0; i <= width; i++) c.drawLine(i * CELL_SIZE, 0, i * CELL_SIZE, height * CELL_SIZE); for (int j = 0; j <= height; j++) c.drawLine(0, j * CELL_SIZE, width * CELL_SIZE, j * CELL_SIZE); } Why <=? Because if there are n cells, we want n+1 lines in the grid (to include the edges) Draw the horizontal grid lines (constant y-values, full width)

Animating the display The only public method is the one that allows the user to specify that they want to see n generations of “The Game of Life”; it is a simple loop that asks the Life object to calculate the next generation, displays it, and waits briefly to give an “animated” effect public void animate(int n) { for (int i=0; i < n; i++) { life.nextGeneration(); display(); c.wait(250); }

Create the Life object

It appears on the workbench

Now create a LifeViewer to view it

The initial randomly chosen configuration is displayed on a SimpleCanvas

After 100 generations, the map looks a bit more “organized”

A new colour scheme can be obtained just by changing the three constants

Responsibility-driven design Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Responsibility-driven design Question: where should we add a new method (which class)? Each class should be responsible for manipulating its own data. The class that owns the data should be responsible for processing it. RDD leads to low coupling. Project 1 example: which class (Person or AddressBook) should calculate the sum of social activities? Why?

Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Localizing change One aim of reducing coupling and responsibility-driven design is to localize change. When a change is needed, as few classes as possible should be affected.

Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Thinking ahead When designing a class, we try to think what changes are likely to be made in the future. We aim to make those changes easy.

Refactoring When classes are maintained, often code is added. Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Refactoring When classes are maintained, often code is added. Classes and methods tend to become longer. Every now and then, classes and methods should be refactored to maintain cohesion and low coupling.

Refactoring and testing Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Refactoring and testing When refactoring code, separate the refactoring from making other changes. First do the refactoring only, without changing the functionality. Test before and after refactoring to ensure that nothing was broken.

Design questions Common questions: Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Design questions Common questions: How long should a class be? How long should a method be? These can now be answered in terms of cohesion and coupling.

Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Design guidelines A method is too long if it does more then one logical task. A class is too complex if it represents more than one logical entity. Note: these are guidelines - they still leave much open to the designer.

Review Programs are continuously changed. Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Review Programs are continuously changed. It is important to make this change possible. Quality of code requires much more than just performing correctly at one time. Code must be understandable and maintainable.

Objects First with Java - A Practical Introduction using BlueJ, © David J. Barnes, Michael Kölling Review Good quality code avoids duplication, displays high cohesion, low coupling. Coding style (commenting, naming, layout, etc.) is also important. There is a big difference in the amount of work required to change poorly structured and well structured code.

Challenge Find out what the model-view-controller design pattern is? You can do a web search to get information, or you can use any other sources you find. How is MVC related to the Game of Life project? What does it suggest? Discuss its application to this project.