CS321 Assignment 4 + CS321L Lab 3

Due: Wednesday March 24th at 11:59 PM. Submit all source (.java) to moodle in a single zip file. Include a README explaining your difficulties if you failed to complete any of the lab 3/assignment 4.

Lab 3: - I have provided most of the lecture material in this handout. Don’t be intimidated by the length of the document, most of it is just background material. Do the problem given in italics, at the end of each question.

Most of the material below was taken from “Effective Java” by Joshua Bloch (3rd edition). If you are interested in a deeper understanding of java best practices, I highly recommend the book.

Q1: Builders - consider using a builder when faced with many constructor parameters. Constructors are limited in that they don’t scale well to large numbers of optional parameters. Consider the following class representing a ‘NutritionFacts’ label:

The labels have a few required fields (serving size, servings per container, calories per serving) and more than 20 optional fields (total fat, saturated fat, trans fat, cholesterol, sodium, etc.). most products have non-zero values for just a few of these optional fields. Traditionally, programmers have used the telescoping constructor pattern, in which a constructor is provided only with the required parameters, another with a single optional parameter, a third with two optional parameters, and so on, culminating in a constructor with all optional parameters. When we want to make an object, we use the constructor with the shortest parameter list containing all parameters we want to set:

This typically requires many parameters we don’t want to set, but are forced to pass anyways (Above we pass a value of ‘0’ for fat). This quickly gets out of hand as the number of parameters increases. 

The solution is to use the Builder pattern: instead of making the desired object directly, the client calls a constructor (or static factory) with all the required parameters and gets a builder object. Then, the client calls setter-like methods on the builder object to set each optional parameter of interest. Finally, the client calls a parameterless build method to generate the object. Here’s how it looks for our NutritionFacts class:

The NutritionFacts class is immutable, and all parameter default values are in one place. The builder’s setter methods return the builder itself so that invocations can be chained, resulting in a fluent API. Here’s how the client code looks:

This client code is easy to write and, more importantly, easy to read. The Builder pattern simulates named optional parameters as found in Python and Scala.

Q1: use the ‘Builder’ pattern somewhere in assignment 4 (for example, when constructing your CenterPane Layout). 

Q2: methods common to all objects

Overriding ‘equals’ seems simple, but there are many ways to get it wrong. The easiest way to get it right is not override equals, in which case each instance of a class is equal only to itself. This is the right thing to do if the following conditions apply:

1) Each instance of the class is inherently unique (such as for Threads, which represent active entities rather than values)

2) No need for class to provide a ‘logical equality’ test

3) Superclass already overrides equals, and superclass behavior is appropriate for subclass (most Set implementations inherit equals from Abstract set, for example).

4) Class is private, and you are certain equals will never be invoked. 

It is appropriate to override equals when implementing a value class such as Integer or String – if you compare two Integer objects, you want equals to return true if they have the same value.

Always override hashcode when you override equals. You must override hashcode in every class that overrides equals. Failure to do so violates the general contract for hashcode, preventing your class from functioning correctly in collections such as HashMap and HashSet. The general contract for hashCode is as follows:

1) When the hashCode method is invoked on an object repeatedly during an execution of an application, it must consistently return the same value, provided no information used in equals comparisons is modified.

2) If two objects are equal according to the equals(Object) method, then calling hashCode on the two objects must produce the same integer result.

3) If two objects are unequal according to the equals(Object) method, it is not required that calling hashCode on each of the objects must produce distinct results. However, the programmer should be aware that producing distinct results for unequal objects may improve the performance of hash tables.

Equal objects must have equal hash codes! (provision 2). Example: consider the following PhoneNumber class:

…and suppose you attempt to use instances of this class as keys in a HashMap:

At this point, you might expect m.get(new PhoneNumber(707, 867, 5309)) to return "Jenny", but instead, it returns null. Notice that two PhoneNumber instances are involved: one is used for insertion into the HashMap, and a second, equal instance is used for (attempted) retrieval. The PhoneNumber class’s failure to override hashCode causes the two equal instances to have unequal hash codes, in violation of the hashCode contract. Therefore, the get method is likely to look for the phone number in a different hash bucket from the one in which it was stored by the put method. Even if the two instances happen to hash to the same bucket, the get method will almost certainly return null, because HashMap has an optimization that caches the hash code associated with each entry and doesn’t bother checking for object equality if the hash codes don’t match.

Fixing this problem is as simple as writing a proper hashCode method for PhoneNumber. So what should a hashCode method look like? It’s trivial to write a bad one. This one, for example, is always legal but should never be used:

This is legal, but extremely bad because every object will hash to the same bucket. Ideally, a hash function should distribute any reasonable collection of unequal instances uniformly across all int values. Achieving this ideal can be difficult.

Luckily it’s not too hard to achieve a fair approximation. Here is a simple recipe:

1. Declare an int variable named result, and initialize it to the hash code c for the first significant field in your object, as computed in step 2.a. (Recall from Item 10 that a significant field is a field that affects equals comparisons.)

2. For every remaining significant field f in your object, do the following:

a. Compute an int hash code c for the field:

i. If the field is of a primitive type, compute Type.hashCode(f), where Type is the boxed primitive class corresponding to f’s type.

ii. If the field is an object reference and this class’s equals method compares the field by recursively invoking equals, recursively invoke hashCode on the field. If a more complex comparison is required, compute a “canonical representation” for this field and invoke hashCode on the canonical representation. If the value of the field is null, use 0 (or some other constant, but 0 is traditional).

iii. If the field is an array, treat it as if each significant element were a separate field. That is, compute a hash code for each significant element by applying these rules recursively, and combine the values per step 2.b. If the array has no significant elements, use a constant, preferably not 0. If all elements are significant, use Arrays.hashCode.

b. Combine the hash code c computed in step 2.a into result as follows: result = 31 * result + c;

3. Return result.

Question 2: override the hashCode() method from PhoneNumber class to adhere to the general contract for hashCode and also provide good performance in the hash table (follow the 3 steps above).

Q3: class design best-practices

Classes and interfaces lie at the heart of the Java programming language. They are its basic units of abstraction. When designing our classes, we should adhere to the following tenets:

1) Minimize the accessibility of classes and members – a well designed class hides all its implementation details, cleanly separating its API from its implementation. Classes should communicate only through their APIs and be oblivious to each other’s inner workings. This concept, known as information hiding or encapsulation is a fundamental tenet of software design. Information hiding allows us to decouple the components that comprise a system, allowing us to develop, test, use, modify, and understand the components in isolation. Classes can be debugged or replaced with little fear of harming the rest of our program. The rule of thumb is simple: make each class or member as inaccessible as possible. In other words, use the lowest possible access level consistent with the proper functioning of the software you are writing.

For member fields, there are 4 possible access levels (listed here in order of increasing accessibility):

• private—The member is accessible only from the top-level class where it is declared. 

• package-private—The member is accessible from any class in the package where it is declared. Technically known as default access, this is the access level you get if no access modifier is specified (except for interface members, which are public by default).

• protected—The member is accessible from subclasses of the class where it is declared (subject to a few restrictions [JLS, 6.6.2]) and from any class in the package where it is declared.

• public—The member is accessible from anywhere.

After carefully designing your class’s public API, your reflex should be to make all other members private. Only if another class in the same package really needs to access a member should you remove the private modifier, making the member package-private. If you find yourself doing this often, you should reexamine the design of your system to see if another decomposition might yield classes that are better decoupled from one another. Instance fields of public classes should rarely be public.

2) In public classes, use accessor methods, not public fields – occasionally, you will be tempted to write degenerate classes that serve no purpose other than to group instance fields:



Because the data fields of such classes are accessed directly, these classes do not offer the benefits of encapsulation. You can’t change the representation without changing the API, you can’t enforce invariants, and you can’t take auxiliary action when a field is accessed. Hard-line object-oriented programmers feel that such classes are anathema and should always be replaced by classes with private fields and public accessor methods (getters) and, for mutable classes, mutators (setters):

If a class is accessible outside its package, provide accessor methods (to preserve the flexibility to change the class’s internal representation). However, if a class is package-private or a private nested class, there is nothing inherently wrong with exposing its data fields. Also, while its never a good idea for a class to expose fields directly, it is less harmful if the fields are immutable (final). For example, the below class guarantees that each instance represents a valid time:

3) Minimize mutability - An immutable class is simply a class whose instances cannot be modified. All of the information contained in each instance is fixed for the lifetime of the object, so no changes can ever be observed. The Java platform libraries contain many immutable classes, including String, the boxed primitive classes, and BigInteger and BigDecimal. There are many good reasons for this: Immutable classes are easier to design, implement, and use than mutable classes. They are less prone to error and are more secure. To make a class immutable, follow these five rules:

1. Don’t provide methods that modify the object’s state (known as mutators).

2. Ensure that the class can’t be extended. This prevents careless or malicious subclasses from compromising the immutable behavior of the class by behaving as if the object’s state has changed. Preventing subclassing is generally accomplished by making the class final.

3. Make all fields final. This clearly expresses your intent in a manner that is enforced by the system. Also, it is necessary to ensure correct behavior if a reference to a newly created instance is passed from one thread to another without synchronization, as spelled out in the memory model.

4. Make all fields private. This prevents clients from obtaining access to mutable objects referred to by fields and modifying these objects directly. While it is technically permissible for immutable classes to have public final fields containing primitive values or references to immutable objects, it is not recommended because it precludes changing the internal representation in a later release.

5. Ensure exclusive access to any mutable components. If your class has any fields that refer to mutable objects, ensure that clients of the class cannot obtain references to these objects. Never initialize such a field to a client-provided object reference or return the field from an accessor. Make defensive copies in constructors, accessors, and readObject methods.

Here is an example of an immutable class ‘Complex’ which represents a complex number:

Immutable objects provide the following advantages:

• Immutable objects are simple.

• Immutable objects are inherently thread-safe; they require no synchronization.

• Immutable objects can be shared freely, and can expose their internals.

 Immutable objects make great building blocks for other objects.

The major disadvantage of immutable objects is they require a separate object for each distinct value.

4) Favor composition over inheritance - Inheritance is a powerful way to achieve code reuse, but it is not always the best tool for the job. Used inappropriately, it leads to fragile software. It is safe to use inheritance within a package, where the subclass and the superclass implementations are under the control of the same programmers. It is also safe to use inheritance when extending classes specifically designed and documented for extension (Item 19). Inheriting from ordinary concrete classes across package boundaries, however, is dangerous.

Inheritance violates encapsulation. In other words, a subclass depends on the implementation details of its superclass for its proper function. The superclass’s implementation may change from release to release, and if it does, the subclass may break, even though its code has not been touched. As a consequence, a subclass must evolve in tandem with its superclass, unless the superclass’s authors have designed and documented it specifically for the purpose of being extended. To make this concrete, let’s suppose we have a program that uses a HashSet. To tune the performance of our program, we need to query the HashSet as to how many elements have been added since it was created (not to be confused with its current size, which goes down when an element is removed). To provide this functionality, we write a HashSet variant that keeps count of the number of attempted element insertions and exports an accessor for this count. The HashSet class contains two methods capable of adding elements, add and addAll, so we override both of these methods:

This class looks reasonable, but it doesn’t work. Suppose we create an instance and add three elements using the addAll method. Incidentally, note that we create a list using the static factory method List.of, which was added in Java 9; if you’re using an earlier release, use Arrays.asList instead:

We would expect the getAddCount method to return three at this point, but it returns six. What went wrong? Internally, HashSet’s addAll method is implemented on top of its add method, although HashSet, quite reasonably, does not document this implementation detail. The addAll method in InstrumentedHashSet added three to addCount and then invoked HashSet’s addAll implementation using super.addAll. This in turn invoked the add method, as overridden in InstrumentedHashSet, once for each element. Each of these three invocations added one more to addCount, for a total increase of six: each element added with the addAll method is double-counted.

We could “fix” the subclass by eliminating its override of the addAll method. While the resulting class would work, it would depend for its proper function on the fact that HashSet’s addAll method is implemented on top of its add method. This “self-use” is an implementation detail, not guaranteed to hold in all implementations of the Java platform and subject to change from release to release. Therefore, the resulting InstrumentedHashSet class would be fragile. It would be slightly better to override the addAll method to iterate over the specified collection, calling the add method once for each element. This would guarantee the correct result whether or not HashSet’s addAll method were implemented atop its add method because HashSet’s addAll implementation would no longer be invoked. This technique, however, does not solve all our problems. It amounts to reimplementing superclass methods that may or may not result in self-use, which is difficult, time-consuming, error-prone, and may reduce performance. Additionally, it isn’t always possible because some methods cannot be implemented without access to private fields inaccessible to the subclass.

There is a way to avoid all of the problems described above. Instead of extending an existing class, give your new class a private field that references an instance of the existing class. This design is called composition because the existing class becomes a component of the new one. Each instance method in the new class invokes the corresponding method on the contained instance of the existing class and returns the results. This is known as forwarding, and the methods in the new class are known as forwarding methods. The resulting class will be rock solid, with no dependencies on the implementation details of the existing class. Even adding new methods to the existing class will have no impact on the new class. To make this concrete, here’s a replacement for InstrumentedHashSet that uses the composition-and-forwarding approach. Note that the implementation is broken into two pieces, the class itself and a reusable forwarding class, which contains all of the forwarding methods and nothing else:

The design of the InstrumentedSet class is enabled by the existence of the Set interface, which captures the functionality of the HashSet class. Besides being robust, this design is extremely flexible. The InstrumentedSet class implements the Set interface and has a single constructor whose argument is also of type Set. In essence, the class transforms one Set into another, adding the instrumentation functionality. Unlike the inheritance-based approach, which works only for a single concrete class and requires a separate constructor for each supported constructor in the superclass, the wrapper class can be used to instrument any Set implementation and will work in conjunction with any preexisting constructor:

The InstrumentedSet class can even be used to temporarily instrument a set instance that has already been used without instrumentation:

The InstrumentedSet class is known as a wrapper class because each InstrumentedSet instance contains (“wraps”) another Set instance. This is also known as the Decorator pattern because the InstrumentedSet class “decorates” a set by adding instrumentation. Sometimes the combination of composition and forwarding is loosely referred to as delegation. Technically it’s not delegation unless the wrapper object passes itself to the wrapped object.

If you use inheritance where composition is appropriate, you needlessly expose implementation details.

The remaining tenets are as follows: (read about them in the Joshua Bloch textbook linked at the start)

5) Design and document for inheritance, or else prohibit it

6) Prefer interfaces over abstract classes

7) Design interfaces for posterity

8) Use interfaces only to define types

9) Prefer class hierarchies to tagged classes

10) Limit source files to a single top-level class

Question 3: make use of the principles outlined above in your assignment 4. At the very least, you should minimize accessibility and mutability of all classes and members, and provide accessor methods instead of public fields. 

Assignment 4 – custom layouts and UI

In this assignment, you will use Java’s built in 2D library (Swing) to create your own UI library which supports the following elements and layouts:

Elements:

1) TextView – a small rectangular box containing some text:

2) Button – a small rectangular box that can be clicked and performs some functionality when clicked:

3) Slider - an adjustable slider:

4) TextEdit – a small rectangular box containing text, when clicked, “Enter text here” appears in the box and the user’s keyboard input is recorded into the box.:

Layouts:

1) LinearLayouts:

Vertical – stack elements from top of screen to bottom of screen

Horizontal – stack elements from left side of screen to right side of screen

2) CenterPaneLayout – a central panel with surrounding elements


You must not use any of Java’s built in buttons or other UI elements or layouts. You must create all your layouts and buttons from scratch. Everything should be drawn on a single JPanel, so you will need to track the positions of your elements using internal fields of your layout classes. You may only use the JPanel (for drawing on), the JFrame (for holding your panel) and the primitive 2D graphics libraries (like g.drawRectangle, etc.) to construct your UI. 

example – your ‘Button’ class should have a ‘draw’ function that takes in ‘Graphics2D g2’ object and uses it to draw the button, for example with g2.fillRect followed by g2.drawText (to color the button and then draw the label). The position of the button will be given by a private member field (int) in the button class, which is determined by the position of the layout containing the button.

You will make use of inheritance and interfaces in this assignment. Consider implementing an abstract class ‘RectangularLayoutElement’ which TextView, Button, Slider, and TextEdit all inherit from. The RectangularLayoutElement class could, for example, contain the position and size of the layout element. You would then extend RectangularLayoutElement and implement the ‘draw’ function to the button specifications. Consider also adding some type of ‘clickable’ or ‘selectable’ interface which buttons, sliders, and textEdit classes implement. Add more interfaces and abstract classes as necessary.

Your elements must be responsive to mouse clicks and keystrokes in the following manner: 

TextView, Button: if user clicks one of these elements with the mouse, simply flash a short color change on the element. 

Slider: user must be able to adjust the slider by clicking and dragging the mouse. This will change the position of the slider and also update an internal variable indicating the slider’s state. 

TextEdit: if user clicks in TextEdit with mouse, change background color of TextEdit and any keyboard input entered by the user will go inside the TextEdit until they deselect it by clicking elsewhere or pressing ‘enter’ or ‘escape’ key.

You must be able to add ‘layouts within layouts’. So for example, if I want to construct a 3x3 grid of buttons, I can first add 3 vertical layouts and then to each of the vertical layouts, I can add three buttons.

Your custom UI must support both dark and light themes.

For the final project, you will combine your custom UI with your back-end logic from assignment 3 (fetching data, displaying chart, etc.) to create your trading dashboard, so be sure to complete this assignment well.

Constructing a layout will probably require quite a few arguments (parent layout, width, height, etc.). When constructing your layouts, you may want to make use of the builder pattern (q1 above).

Be sure to implement the principles from q1 and q3 in the above lab. Your code will be graded based on both functionality (you must complete all parts of the assignment) as well as usage of OO principles (Builder pattern, minimizing mutability and accessibility, etc.).