CSC 321.01, Class 19: Design patterns, continued
Overview
- Preliminaries
- Notes and news
- Upcoming work
- Extra credit
- Questions
- Dependency injection
- Observers
- Factories
- Strategy
- Prototype
News / Etc.
- Your pattern homeworks reminded me of why the Interweb is a danger.
- We have two possible approaches to the last few days of class. I’ll
let you choose between them.
- Option 1: Work on a project, present next Monday, debrief next Wednesday.
- Option 2: Wednesday and Friday off, some topic next Monday, debrief next Wednesday.
- Option 3: Every student can choose whether they want to do option 1 or option 2.
- You chose option 3; I’ll send you the project by the end of the day.
- Yes, you can decide whether or not to do the project based on what the project is.
- No, the project cannot lower your grade in this course.
Upcoming work
- Optional project.
Good things to do
- Lots of things in the Rosenfield Symposium on Technology and Human Rights.
- CS Table, Tuesday at noon, Generating poetry
- CS Extras, Thursday at 4:15 pm, The CS Curriculum
Design patterns
- When we encounter similar problems, we often have similar solutions.
- Patterns represent those problem/solution pairs
Dependency injection
Problem: Your program depends on a service of some sort. Many such services may be available, not always with the same interfaces/capabilities. How do we design our program so that it’s easy to switch/set which service to use?
Bad solution (anti-pattern)
class Client
{
Service1 service1 = ...;
Service2 service2 = ...;
Service3 service3 = ...;
Service4 service4 = ...;
String choiceOfService;
....
if (choiceOfService.equals("service1")) {
service1.op(stuff);
}
else if (choiceOfService.equals("service2")) {
service2.operation(stuff,morestuff);
}
else if (choiceOfService.equals("service3")) {
service3.op(stuff,morestuff);
}
else if (choiceOfService.equals("service4")) {
service4.procedure(stuff,morestuff,evenmorestuff);
}
else {
throw new Exception("Unknown service: " + choiceOfService);
}
}
- Why is this a bad solution?
- Not very DRY. The lines are similar. (We could make an array of servicenames and services, and use a loop.)
- To add another service, we have to change a lot of code.
- It’s a pain to read.
- To remove a service, we may have to change a lot of code.
- All those tests are somewhat inefficient.
- The client needs to keep track of a lot of services; that means when we make changes to those services, we have conversations we need to have.
What is a better solution?
class Client
{
Service service;
...
service.doThing(allparamsthatmaybenecessary);
}
interface Service
{
void doThing(...);
}
class Service1Adapter
extendsService1
implements Service
{
public void doThing()
{
super.op(subset_of_allparams);
} // doThing
}
This is nice because it allows us to easily swap services.
- Question: How does the client learn about the service?
- When we construct the client, we provide the service.
- Clients have a
setService()
method. - The client can read the service from a particular location on disk.
- The client can query a “meta service” for available services.
- Why is this called “dependency injection”?
- Client depends on the Servers. We are injecting something in the middle of that dependency.
- We are injecting the dependency into the client.
- Which of the patterns we just learned seems close to dependency injection?
- Claim: Prototype, because we’re looking at objects that may change their behavior.
- Claim: Factory, because we’re potentially building one of several subtypes of objects. It also has a similar problem statement.
- Claim: Strategy, because you’re using an interface to allow you to swap in different methods.
- Claim: Not observer, but observers do have the same “extract dependencies.”
Observers
Problem: A one-to-many relationship, where you have a subject and one or many observers that want to know when the subject changes state.
Example: Almost any event driven system.
Concrete example: Twitter
Note: It’s much better to have the subject inform the observers than to have the observers repeatedly query the subject.
Note: Useful for MVC.
Solution: Subjects keep track of their observers. We typically have
an addObserver
method. Each Observer has a notifyMe
method that
the Subject is supposed to call. In the implementation of Subject,
we need to make sure to call notifyMe for each observer.
Note: The list of observers is probably an ArrayList.
Note: Subjects ahve a variety of methods that may change their state.
Note: If we have addObserver
, we should probably have removeObserver
.
Note: Sometimes we still want to permit observers to directly query the subject.
Factories
Problem: Want to make a program portable across multiple operating systems and databases and what to make that portability clear.
Problem, more general: You want to produce an object in some class, but you aren’t always sure what subtype of the class you want, you might want a factory.
if (type.equals("circle"))
thingy = new Circle(...);
else if (type.equals("square"))
thingy = new Square(...);
vs.
interface Factory
{
Thing create(...);
}
public class CircleFactory
{
Thing create(...)
{
return new Circle();
}
}
thingy = myfactory.create(...);
Concrete example: Video games
Strategy
Problem: We might have different algorithms that are similar. Choosing which one can be complex /hard to read.
Bad strategy: A long case statement.
Example: Sorting.
Solution: Encapsulate them in an interface-like thing. Have general calls.
public interface Sorter<T implements Comparator>
{
public void sort(T[] vals);
} // Sorter<T>
public class SelectionSorter implements Sorter
{
}
public class LuckSorter implements Sorter
{
}
Prototype
Problem: Creating a new object is expensive, so we can grab an object without having to create a new one.
Example: Grabbing data from the database. Instead of grabbing it again, clone the object that already has the data. Kind of like a flyweight.
Prototype: A different model of making objects. Traditionally: We define
classes and call new. We also define subclasses. But what when we want
“one-off” subclasses? Start with objects. Every object has clone
.
Every object has seams, so you can add new fields, methods.