CS 3500 Fall 2013 Lecture 1: Review of Java ------------------------- Goals of this course: --------------------- The goals of this course is to make you a better programmer by reflecting on what you have learned so far and learning how to design program components that can be reused in a variety of settings. Designing reusable components is the key to increasing the productivity, security, and reliability of computer systems. The design of reusable components requires that the programmer understands and respect the 'abstraction barrier' --- the clear separation between the specification of the component behavior and its implementation. Another goal is to reflect on our programming practices and learn techniques and concepts that will help us improve the program's performance, reliability, and design through refactoring, regression testing, debugging, through the use of some commonly known algorithms and algorithm design techniques (such as memorization, pre-computation, divide and conquer…). Finally, we will look at the common design patterns in the object-oriented style that are used to accomplish these tasks, as well as some of the software design practices and strategies used in designing more complex software artifacts. Review of Java from Fundies 2: ------------------------------ One of the recurring themes in Fundies 2 has been the gradual design of abstractions following the 'Design Recipe for Abstractions' in the context of learning new Java constructs and techniques. We quickly review the key ideas and the abstractions they represent, then look at Java features we have omitted or under-emphasized in Fundies 2. Design Recipe for Abstractions: ``````````````````````````````` 1. Look at two or more code segments that look very similar. (The goal is to eliminate repeating the same code in several places.) Identify the places where these code segments differ and highlight the differences. 2. Turn the differences into parameters, and refactor the original code so it relies on these parameters. 3. Convert the examples and tests for the original problem into code that invokes the refactored variant, by providing the appropriate values for the parameters. 4. Run the original tests and make sure all of them pass. Places where we have used the Design Recipe for Abstractions: ````````````````````````````````````````````````````````````` -- abstract classes Our examples involved the IShape interface with several types of shapes, e.g. Circle (with its location, radius, color), Rectangle (with its location, width, height, color). The original class diagram was: +--------+ | IShape | +--------+ / \ --- | ------------------- | | +-------------+ +-------------+ | Circle | | Rect | +-------------+ +-------------+ | CartPt loc | | CartPt loc | | int radius | | int width | | Color color |-+ | int height | +-------------+ | | Color color |-+ | +-------------+ | +--------+ +---------+ | | v v +--------+ | CartPt | +--------+ | int x | | int y | +--------+ In the classes Circle and Rectangle we have defined methods with the following signatures: double area() -- computes the area of this shape double distTo0() -- computes the distance of this shape to the origin boolean closerTo0(IShape that) - is this shape closer to the origin than the given one? void move(int dx, int dy) -- change the location of this shape by the given dx dy By designing the abstract class AShape, we could lift two fields from these classes to the abstract class (CartPt loc and Color color), we could implement the last two methods as concrete methods in the abstract class. We have also introduced 'overriding' of methods - in the case where the concrete method in the abstract class (or just super class) worked fine for several variants of the union but needed adjustment in one or two special cases. Dynamic dispatch: ````````````````` How does Java know which area method to invoke when a shape invokes it? For example, we have AShape cir = . . . and later in the code we see cir.area() Well, it depends on the 'runtime type' of 'cir', i.e. the type of object this variable represents. If it represents a Circle, the method will be selected from the class Circle; if it is a Rectangle, the method defined in the Rectangle class will be invoked. But in the case cir.move(dx, dy) the run-time byte code will not be able to find the method 'move' in the 'Circle' class, and so it looks in its super class - and repeats searching in the super classes until the method is found. If not, the program throws an 'Exception' indicating that the desired method does not exist. Function objects: ````````````````` We started by designing several methods that would consume a list of data and produce a list of those items that had some desired property. The code differed only in how we determined whether an object had the desired property. This lead us to introducing 'function objects' - a rather unfortunate feature of Java whose designers forgot about the lessons from Scheme about the importance of functions being first class objects. Here we design a method 'filter' that would produce a list of items that satisfy some predicate, and the predicate has been defined as an instance of a class that implemented the interface that contained just one predicate method. We will return to this topic in a couple of weeks - meanwhile, just think of what this means. There will be several interesting issues we will get to when discussing function objects: the Singleton pattern, the inner classes and anonymous inner classes, as well as possibly the Factory method. Parametrized types: ``````````````````` We have seen that the Java Collection Framework defines a number of classes that represent the common organization of data sets, such as ArrayList and HashMap and uses the type parameters to specify the type of data the set contains. Again, we will return to the parametrized types later in the course. Abstract Data Types: ```````````````````` Fundies 2 gave us only a preview of this topic - it will be studied in depth in this course. We have implemented most of the methods for a data set that is organized as a stack - and provided more than one implementation of the same behavior. Iterators: `````````` We have also seen that we can implement the 'Iterator' interface for several different implementation of a data set, and once we have that a number of methods that rely on traversing the data set in a systematic order will work equally well with the provided 'Iterator'. Again, this is a topic we will study in depth during this semester. Information hiding: ------------------- Towards the end of the course we introduced the 'visibility modifiers': public, private, and protected. We actually ignored the third option. The private option was used to hide some aspects of the implementation of our program from the program's user. Designing programs in such a way that the user can only see and use a carefully designed set of methods and possibly fields or constants is at the heart of designing reusable programs - and we will make sure this is well understood and used in practical situations. We hardly mentioned 'packages' - a way to group together several inter-related and interacting classes, so that the whole 'package' can be distributed (and used) as one entity. Again, we will make this concept an important part of understanding the design of reusable programs. With this come the 'import' statements - these are used to tell the program to use the class definition fro the specified package. Static members: ``````````````` In Java we can define methods and fields within a class that are common to all instances of the class and do not rely on the particular properties of the individual instances. The method that computes the minimum of the two given numbers does not need to know anything about the class where it has been defined. In a program that represents an interactive game the width and the height of the 'Canvas" is the same for the duration of the game - and so should be defined as a 'constant' - static field. Again, while we mentioned this in Fundies 2, we will pay great attention to this topic in this course. Of course, we have seen, but just barely, the method with the header public static void main(String[] argv) This is the method Java runtime looks for to start executing your program. Every Java application has such method - it may have several of them, and the program may do different things deeding on where it started executing. We will do a lot with the static members and methods. Equals and hashCode: ```````````````````` In the past we relied on the tester library to design and evaluate tests for our programs. In the production environment it will be our responsibility to design the methods that compare the equality of two values (equals method) - and we have learned already that in Java it should always be coupled with a corresponding hashCode method. Furthermore, we need to learn how to design a test harness - the program that runs the tests we specify, and produces a report on which tests failed and why.