470 likes | 645 Views
Design Patterns in Java Part II Responsibility Patterns Chapter 7 Introducing Responsibility. Summary prepared by Kirk Scott. Introducing Responsibility. The topic of responsibility in design patterns can be very briefly summarized as: “Which class implements which functionality?”
E N D
Design Patterns in JavaPart IIResponsibility PatternsChapter 7Introducing Responsibility Summary prepared by Kirk Scott
Introducing Responsibility • The topic of responsibility in design patterns can be very briefly summarized as: • “Which class implements which functionality?” • When considered as part of the relationships between classes, responsibility has this additional connotation: • “Is one class in the position of receiving messages requesting some action, and also in the position of passing requests on and delegating the performance of the actions to other classes?”
A basic default baseline is that objects are independent and self-sufficient • The responsibility design patterns tend to cover cases which diverge from the default • There are patterns which embody the following, rather generally stated characteristics
Responsibility for performing a given action may be centralized (passing a request to a central class/object for performance) • Responsibility for performing a given action may be escalated (passing a request up a hierarchy) • A given object may be isolated, limiting its responsibility (implicitly putting responsibility elsewhere)
Responsibility turns out to be part of a much broader question of object-oriented design • It comes back to the fundamental question of what classes should be in a design and which functionalities should be in which classes • The book takes the attitude that programmers develop an intuitive feeling for this, but probably can’t express it very clearly • It pursues this idea with a few challenges
Challenge 7.1 • The class structure shown in Figure 7.1 has at least ten questionable assignments of responsibility. Circle as many problems as you can find; then write a statement about what is wrong for four of these points. • Comment mode on: • Some of the problems would only be evident to someone who knew the problem domain. • Others are so gross and weird that the only reaction might be, “Huh?”
Solution7.1 • The Rocket.thrust() method returns a Rocket instead of some type of number or physical quantity. • The LiquidRocket class has a getLocation() method, although nothing in the diagram or in the problem domain suggests that we model rockets as having a location. Even if we did, there is no reason for liquid-fueled rockets, but not other Rocket objects, to have a location
The isLiquid() method may be an acceptable alternative to using the instanceof operator, but then we’d expect the superclass to also have an isLiquid() method that would return false. • CheapRockets is plural, although class names are conventionally singular.
The CheapRockets class implements Runnable, although this interface has nothing to do with cheap rocket objects from the problem domain. • We could model cheapness with attributes alone, so there is no justification for creating a class just for cheap rockets.
The CheapRockets class introduces a factoring that conflicts with factoring the rocket model as liquid or solid. For example, it is not clear how to model a cheap liquid rocket. • The model shows that Firework is a subclass of LiquidRocket, implying that all firewords are liquid rockets, which is false.
The model shows a direct relation between reservations and types of firework, although no such relation exists in the problem domain. • The Reservation class has its own copy of city, which it should get by delegation to a Location object. • CheapRockets is composed of Runnable objects, which is simply bizarre.
End of Solution 7.1; on to the next topic • Looking at all of the bad elements of the previous design may put you in the mood to try and define a good class design • In order to define a design you end up needing agreement on terminology between users and developers • The authors state that defining terms is useful if it helps people to communicate • They warn that its usefulness decreases when it becomes a goal in itself and leads to conflict
Challenge 7.2 • Define the qualities of an effective, useful class.
Solution 7.2 • The value of this challenge is not to get the right answer but rather to exercise your thinking about what makes up a good class. Consider whether your definition addresses the following points. • A nuts-and-bolts description of a class is: “A named collection of fields that hold data values and methods that operate on those values” [Flanagan 2005, p. 71]
A class establishes a collection of fields; that is, it defines the attributes of an object. The attribute types are other classes, primitive data types, such as boolean and int, or interfaces. • A class designer should be able to justify how a class’s attributes are related. • A class should have a cohesive purpose.
The name of a class should reflect the meaning of the class both as a collection of attributes and with respect to the class’s behavior. • A class must support all the behaviors it defines, as well as all those in superclasses and all methods in interfaces that the class implements. (A decision to not support a superclass or an interface method is occasionally justifiable.)
A class should have a justifiable relationship to its superclass. • The name of each of a class’s methods should be a good commentary on what the method does.
End of Solution 7.2; on to the next topic • The last point of solution 7.2 was the usefulness of descriptive method names • On the other hand, method names do not always fully explain what the method does
Challenge 7.3 • Give an example where, for good reason, the effect of calling a method cannot be predicted from the method’s name. • Comment mode on: • This is not really such a difficult question, although the book provides a rather long and involved answer
Solution 7.3 • For a change I don’t give the book’s answer in its entirety • Three basic points are sufficient to summarize the idea
1. The effect of calling a method on an object depends on what class the object is an instance of • This is a result of dynamic binding • It may be necessary to sort through the various implementations of a method to get a clear picture of what it does for a particular class
2. The effect of calling a method on an object also depends on the state of the object • A method may be coded in such a way that it makes use of the current values of the instance variables in order to reset those values or return a certain value
3. At other times the effect of a method may not be immediately clear because the name is unfamiliar • This can result from situations where you are using an interface developed by a different programmer
The assignment of responsibility among classes of a design seems to be more of an art than a science • There are apparently many weak software designs that could be improved by better assignment of responsibility • In general, clearly defining responsibilities for classes and methods, and correctly implementing them would be a good start towards a sound system design
Controlling Responsibility with Visibility • The authors point out that the responsibility for making sure classes and methods work as they should falls on the programmer • It turns out that the implementation of responsibility hinges at least in part on encapsulation • Closely related to the concept of encapsulation is the concept of visibility • Visibility refers to the access modifiers (public, private, protected, package) applied to components of Java code
The basic idea is this: • By using access modifiers to limit visibility, you reduce the number of things that client code can do with objects of your class • This, in turn limits your responsibility • If everything were global, you would not be in a position where you could responsibly offer any services • A client could do anything at all
This table summarizes the access modifiers in Java and their meanings
The authors emphasize that the descriptions in the table are informal • It is also important to have a grasp of their formal meaning • The authors pose an interesting question • It has to do with the level at which visibility is applied • Is this a class level construct or an object level construct in Java?
Challenge 7.4 • Can an object refer to a private member of another instance of the same class? Specifically, will the following code compile? • Comment mode on: • See the code on the next overhead • I have fleshed out their code and modified it so that the authors’ coding style doesn’t mislead you
public class Firework • { • private double weight; • public Firework(double weightIn); • { • weight = weightIn; • } • public double compare(Firework fireworkIn) • { • return this.weight – fireworkIn.weight; • } • }
What the book is driving at is this: • Is the expression fireworkIn.weight valid? • The method compare() would be called on an instance of the Firework class • The explicit parameter passed in is also of the type Firework • Within the body of the method, do you have direct access to the private instance variable weight belonging to fireworkIn?
Solution 7.4 • The code compiles with no problems. Access is defined at a class level, not an object level. So, for example, one Firework object can access another Firework object’s private variables and methods.
Comment mode on: • Of course it bloody well works • We’ve been doing this all along • If you are working with an object of a given class within the code of that class, you have direct access • You get so used to this that when the question is posed directly, the answer may not occur to you
However, it is a question worth asking and the book’s extended answer is worth considering • You get brainwashed into the idea that encapsulation is good and one “thing” should be protected from another • If encapsulation is so good, why isn’t one object of a class protected from another object of the same class? • Not to worry, the previous question is rhetorical
The point is to recognize that in the overall design of Java, access modifiers apply at the class level, not the object level • It’s not clear that finer grained access control would gain you anything • It is less clear how you might accomplish that goal without complicated new constructs and syntax • It’s even less clear that you could successfully write/read/understand code that had such limited visibility built in
The book now starts arguing the other side of the question • I have always maintained that instance variables should be private and methods should be public • The explicit use of the protected access modifier came up in the discussion of the clone() method • Notice that with respect to methods, protected is more limited than public
However, the book suggests that it may be possible to declare some instance variables protected rather than private • This eases limitations on visibility • For example, it would give subclasses direct access to their inherited instance variables • In some cases this might be worth doing • In a few instances the authors have done something like this and it has made for simpler code (especially in subclasses)
This may increase the utility and ease of writing additional code • It may also increase the mischief that other code writers can create • Overall, it decreases the limitation of responsibility for your code • In other words, using the authors’ terminology for this chapter, by giving other code writers more opportunity, you are increasing your responsibility
However, this is not responsibility in a wholly positive sense, responsibility for yourself • It is a kind of negative responsibility, where you become responsible for the potential evil that other programmers might work on your code • You have to consider this carefully before making a decision to do something like this
Summary • Note, first of all, that material follows the summary • This plan is followed in all of the chapters which introduce a category of design patterns
Summary • Simply stated, a developer is responsible for the inclusion of suitable attributes and the implementation of suitable methods for classes • Along with choosing the right attributes and functionality, the developer should make sure that the names of methods tell what they do, to the extent possible • In certain circumstances you may increase your responsibility by choosing a less limiting access modifier for some of what you implement • Whatever you do, you can’t escape responsibility
Beyond Ordinary Responsibility • Most of the foregoing discussion centered on this idea: • The goal is to develop classes so that objects are independent and self-sufficient • In other words, developer responsibility begins at the class design level • This idea can be summarized as distributed responsibility
The design patterns of the following chapters will expand on this idea • In other words, what patterns exist so that responsibility can be passed along when necessary? • The following table lists different responsibility intents along with the names of the new design patterns which will implement them
The intent of each design pattern is to solve a problem in a context • Collectively, these are the situations that these design patterns address: • Situations where the default goal of distributing responsibility as much as possible will not solve a responsibility problem • It is necessary to centralize or pass responsibility in order to handle it successfully in a given context