cs205: engineering software? |
(none) 05 April 2010 |
1. (7.1 / 10) Which design is better? State clearly the reasons the design you choose is better than the other design.
In Design A, all the datatypes for representing things on the map are subtypes of MapLocation. In Design B, the datatypes that represent things on the map depend on MapLocation, but are not subtypes of it. Which makes more sense? This depends on understanding if the relationship between the other types and MapLocation is a is-a relationship (subtyping) or a has-a relationship (dependency). From the description of map location ("the MapLocation datatype represents a location on the map (for example, using its longitude and latitude)"), it is clear that the has-a relationship is more appropriate. For example, a Road is not a single location on a map; it is a connection between two or more locations. (The following questions make this even more clear).
Very few people answered this correctly. This was not meant to be a trick question, but most answers favored Design A for its apparent simplicity. If you think more carefully about the subtyping relationships (it doesn't make sense for Road to be a subtype of MapLocation), and extensibility (no easy way to add new kinds of places), there is no good argument favoring design A.
Vector<MapLocation> waypoints; Vector<Intersection> intersections; class Intersection { MapLocation loc; Road road; }The abstraction function is straightforward:
AF (rep) = < waypoints, intersections > where waypoints = < rep.waypoints.getAt (0), ..., rep.waypoints.elementAt ([rep.waypoints.size() - 1) > intersections = < < rep.intersections.getAt(0).loc, rep.intersections.getAt(0).road >, ... < rep.intersections.getAt(rep.intersections.size() - 1).loc, rep.intersections.getAt(rep.intersections.size() - 1).road > >The rep invariant needs to ensure that the result of applying the abstraction function to any concrete value that satisfies the rep invariant satisfies the properties stated in the datatype overview. For the abstraction function to work, we need all objects involved to be non-null:
RI(c) = c.waypoints != null and c.waypoints[i] != null for all 0 <= i < c.waypoints.size() and c.intersections != null and c.intersections[i] != null and c.intersections[i].loc != null and c.intersections[i].road != null for all 0 <= i < c.intersections.size ()We also need to know there are at least 2 waypoints (otherwise the road has no length). This requirement was stated explicitly in the overview specification:
and c.waypoints.size() >= 2The tougher constraint is that the intersections must be on the road. This is somewhat clear from the overview specification, and more clear from the addIntersection method sepcification. Expressing it formally is difficult, but we can express it clearly informally:
and for all 0 <= i < c.intersections.size (): c.intersections[i].loc is on the line between c.waypoints[j] and c.waypoints[j+1] for some j and on the line between c.intersections[i].road.waypoints[k] and c.intersections[i].road.waypoints[k+1] for some k and there are no pair of intersections at the same location c.intersections[i].loc = c.intersections[j].loc ==> i == jSome rep invariants also included a constratint that there must be at least one intersection, although there is nothing in the specification that seems to require this.
public Exit findNearestExit (MapLocation loc) throws NoExitException MODIFIES: nothing EFFECTS: If this contains no exits, throws NoExitException. Otherwise, returns an Exit on this such that there is no other exit on this that is geographically closer to loc.Note that this specification makes it clear what "closest" means (geographically closest without regard to the highway route). A more useful function might define "closest" as distance travelled along the highway, and require the location to be on the road.
The race condition is that the colleague of the this object could change between the name.compareTo call in the this object's associated thread and the name.compareTo call in the colleague object thread. Since philsophize is not synchronized, the (synchronized) setColleague method could execute in another thread while philosophize is executing. Then, the wrong lock would be grabbed! Instead of locking our current colleague, we would grab the lock for our previous colleague and then call colleague.argue (which is synchronized, and will wait for the lock on our current colleague).Suggest a way to fix this problem. A good answer will show clearly how you would change the code. Be careful to make sure that your fix does not allow any new deadlock opportunities.
public class Philosopher { private Philosopher colleague; private String name; private String quote; private Object colleagueLock; public Philosopher(String name, String quote) { this.name = name; this.quote = quote; this.colleagueLock = new Object(); } public synchronized void setColleague(Philosopher p) { synchronized (colleagueLock) { colleague = p; } } public synchronized void argue() { ... } // elided public void philosophize () { Object lock1, lock2; if (colleague != null) { // Need a colleague to start an // argument. // Always grab the lock for whichever name is alphabetically // first synchronized (coleagueLock) { if (name.compareTo (colleague.name) < 0) { lock1 = this; lock2 = colleague; } else { lock1 = colleague; lock2 = this; } synchronized (lock1) { synchronized (lock2) { System.err.println (name + "[Thread " + Thread.currentThread().getName () + "] says " + quote); colleague.argue (); } } } } } }
6. (6.7 / 10) According to the PS5 comments (question5), "Even without the race condition, there are ways in which our solution could still deadlock: we could have three philosophers who have non-reflexive colleagues." Explain in detail a scenario where such a deadlock could occur, or explain why no scenario involving three philosophers with non-reflexive colleagues could produce a deadlock.
In fact, the PS5 comments are wrong — non-reflexive colleagues cannot be the cause of a deadlock. With three philosophers we have two situations to consider:
iload_1 iadd(See Class 30 for information on the instructions.)
Data Abstraction. Java provides good support for data abstraction since we can use the class mechanism to package methods and state, and use private visibility modifier to hide the data representation from clients.
Iteration Abstraction. Java (since version 1.5) provides support for iteration abstraction using the Iterator interface. This allows datatypes to provide methods that enable clients to define a loop that iterates over a data collection without revealing the interals of the collection implementation.
Subtype Abstraction. Java supports subtype abstraction with the extends and implements mechanisms (both of which define subtyping relationships), and through the dynamic dispatching of methods. This allows objects of many types that are subtypes of a given type to be used in the same way.
Concurrency Abstraction. Java supports concurrency abstraction by providing threads and synchronization. These mechanisms hide some of the details of event ordering from a programmer, but don't hide the problems of preventing race conditions and deadlocks from the programmer.