Software Development (CSC 321 2016S) : EBoards

CSC321.01 2016S, Class 20: Good Design, Revisited


Overview

Preliminaries

Admin

Questions

Writing prompt

Write a short essay reflecting on the ethics of engineering software as a service, due the last day of class.

Parameters

Design Patterns

Sam's Favorite Patterns

Factory - You have a program (or method or ...) that will need to build lots of widgets - pass in a function/object that does the actual building.

public class HardGame
{
  WidgetMaker widgetMaker;
  public void run()
  {
    int difficulty = 1;
    every second
      {
        Widget w = widgetmaker.spawn(difficulty);
        screen.place(w, 0, 0);
        difficulty += random(2);
      }
  }
} // class HardGame

public interface WidgetMaker
{
  Widget spawn(int difficulty);
} // 

public class HorribleAlien implements Widget
{
  public HorribleAlien(int type, int size, int firepower)
  {
    ...
  }
}
public class HorribleAlienMaker implements WidgetMaker
{
  public Widget spawn(int difficulty)
  {
    return new HorribleAlien(random(3), difficulty*2, difficulty);
}

public class FluffySheepMaker implemets WidgetMaker
{
  ..
}

Factories are really useful when you are doing GUIs.

Adapter/Proxy/....

Problem: You have to write an class/object that meets a particular interface. E.g.,

    // Things you can add to other things of the same type
    public interface Addable<T subtype Addable<T>>
    {
      public T add(T other);
    }

And you have objects that were designed separately from the interface, but should be easily adapted to meet the interface

    public class BigInteger
    {
      public BigInteger add(BigInteger other);
    }

    public class AddableBigInteger implements Addable
    {
      BigInteger contents;
      public AddableBigInteger add(AddableBigInteger other)
      {
        return this.contents.add(other.contents);
      }
    }

    public class AddableBigInteger
      extends BigInteger
      implements Addable
    {
      ...
    }

Singletons, Nulls, etc.

Decorators: Add functionality to all the classes that implement a particular interface.

public interface Comparator<T implements Comparator<T>>
{
  public int compare(T left, T right);
}

public class CountingComparator<...>
  implements Comparator<...>
{
  int count;

  Compartor comp;
  public CountingComparator(Comparator comp)
  {
    this.comp = comp;
  }

 public int getCount()
 {
   return this.count;
 }

 public int resetCount()
 {
   this.count = 0;
 }

 public int compare(T left, T right)
 {
   ++count;
   return this.comp.compare(left, right);
 } 
} //

Old code

    sort(A, new StringComparator());

Now

    sort(A, new CountingComparator(new StringComparator()));

Some Questions

Patterns

The difference between Strategy Pattern and Template Method Pattern was a bit vague. From what I got after all, both deal with general approach towards a given task but with different variants. The Strategy Pattern would analyse the given variant of the task and use different steps to achieve it. On the other hand, the Method Pattern follows the same steps every time, but for different variants the implementation of those steps could differ.

We'll think about this one in terms of sorting.

We know lots of sorting methods: Bubble sort, Radix sort, Quicksort, Insertion sort, Merge sort

    public interface Sorter
    {
      public void sort(...);
    }

    public class Quicksorter implements Sorter
    {
    }

    public class MergeSorter implements Sorters
    {
    }

Most of your code will just work with Sorters. You can substitute in one sorter for another.

That's the strategy pattern.

In implementing any of these sorting routines, we'll probably call the compare method. If we do it right, the compare method is an instance of the template method pattern.

_I found the Adapter, Proxy, Facade, Bridge design pattern to be quite confusing. It states: "Convert the programming interface of a class into another interface that clients expect, or decouple an abstraction’s interface from its implementation, for dependency injection or performance." It seems as though this principle is trying to make the program/class more user intuitive, but I really have no idea what “decoupling an abstraction’s interface from its implementation” looks like. _

To me, these patterns are usd most frequently when you have a class that has one interface, and you essentially want to use it to meet another interface.

To me, the ‘decorator’ pattern was the most confusing. Mostly, I don’t understand why attaching responsibilities dynamically is better design-wise than having them statically linked— wouldn’t that make debugging/understanding harder?

The decorator pattern. From the wikipedia page, the pattern appears to involve wrapping the class in another class which provides the same interface and just calls directly to original class's functions. Why is this helpful?

There are multiple uses of decorator. We'll look at some examples.

Observer: One or more entities need to be notified when something happens to an object. Eg: when the value of a slider changes, change the transparency of an image accordingly.

Many programs have parts that need to know when state changes. We can consider an email reader.

    public interface Observable
    {
      public void addObserver(Observer o);
    }

I found creational patterns most confusing. From what I understand, they seem to provide a means to create object without using the usual method of instantiating using an operator. Therefore, they hide the creation logic somehow. I'm not quite sure how it is possible to hide the creation logic when creating an object.

Why would the Prototype design pattern be useful? Why would it be better to create copies of something instead of creating entirely fresh instances?

We might build info as we go. Think about drawings as values.

SOLID

The most confusing design principle was the SOLID acronym. It seemed like a more confusing SOFA. Single responsibility was familiar, but a few of these, such as the Liskov substitution principle, seem harder to inspect the code for obvious signals that this might be a problem. I think of code smells as something I can quickly identify as a problem just by looking at the code, not necessary tracing it thoroughly. So something like a segment of code never being executed wouldn't really be a code smell, because you would probably have to trace through the code to make sure that there was no way for a line to be executed. The dependency principle also seemed like something that would have to thought of ahead of time, not noticed as a bad practice after having written the code and looking it over. So SOLID principles seemed harder to detect, or requiring more thought to take care of.

Dependency Injection

I'm most confused about the injection principle, because I'm not sure what they mean by inject. What I take this principle to mean is that if two classes are similar, and depend on each other, then you should write a common interface for the two of them to inherit methods from. Why this is, I'm not completely clear. Is this the same type of interface that we talk about when learning Java?

Injection of Dependencies was a bit unclear to me. Most other pieces seemed to be referencing code practices which are relevant at the compilation step, but this one seemed focused on the runtime. If I understand correctly, this is targeted at projects that have multiple implementations of a single class that may be used in different scenarios (e.g. testing vs production). This principle asserted that the different implementations should share dependencies, so I assumed that meant using one binary file for all the dependencies for both implementations since the dependencies shouldn’t be different between them.

The Dependency Inversion Principle confuses me. I think it's trying to tell me that I shouldn't have non-abstract arguments and return values. Shouldn't all of my functions work on the same abstraction level? I don't see the relevancy that the author attributes to this concept.

Demeter

I got confused about the formal language that was explaining the concept. It was a bit too abstract at first glance. Essentially, the Demeter Principle says that classes should only interact with methods that they own. In other words, we don't want to access another class's methods and modify instance variables that belong to an instance of another class. This can create unpredictable problems and we may not clearly know how our classes are interacting.

GoF Patterns

Creational

Structural

Behavioral

SOLID Principles

Intended as a mechanism for thinking about class design. Five basic principles.