Essential Terms


The following terms have the greatest philosophical importance ”they embody most of the philosophy that makes the object difference.

Object

Objects are the fundamental units of understanding. We define the world (and the world of software) in terms of objects. Everything is an object! An object is anything capable of providing a limited set of useful services. Metaphorically, an object is like a human agent or actor. We decompose the complex world around us in terms of objects, and we assemble (compose) objects in various ways so that they can perform useful tasks on our behalf .

Our understanding of an object is, and should be, based on that object s public appearance: its family name (class) and an advertised list of services (protocol) that it is willing to provide. Every object has a personal name, which is a unique identifier, and a family name, which identifies the set (class) of similar objects to which the particular object belongs. Most often we speak of the family (or class) name of the object. That name is descriptive of the object and should convey a general sense of the kind of services it might be able to provide. The set of advertised services usually takes the form of a list of syntactic phrases that we can use to invoke each behavior. Separately documented is a list of potential states that the object might be in. Not all object states are included in this list, only those that the object wants to share and those of which other objects need to be aware. Together these lists constitute our behavioral promise to the world at large. We (speaking as an object) do not share with the world any notion of our internal structure and certainly no sense of how we do what we do.

Every object has access to whatever knowledge is required to perform its advertised services. This does not mean that the object contains that information. Information can also be accessed by asking another object, supplied as part of a request, or calculated upon demand.

Traditionally, software was conceived in terms of passive data and active procedures. One of the more famous definitions of a program is algorithms plus data structures. This is also called the pigeonhole approach to software ”passive bits of data sitting in boxes (such as those seen in a post office sorting room, which are called pigeonholes) where procedures come to remove a bit of data, transform or use it, and then put it back for the next procedure to access.

Because everything is an object, there is no data. One of the principal ideas of traditional software approaches has disappeared. Everything, including characters and integers, is an object responsible for providing specific services. The elimination of passive data has important consequences for object design, a topic that will be discussed in depth later in this chapter. The consequences manifest themselves at the level of design and are felt minimally at the level of code. This should be reassuring to those (a vast majority) who are using typed languages such as Java. When you actually write code and are compelled to declare types for variables or use objects such as strings and characters and numbers solely for their ability to represent data, you won t be violating any principles of object thinking. You will find that you have fewer objects representing passive data and are much more likely to create an abstract data type to represent a data object instead of using constructs built into the language.

Therefore, the three popular graphic models of an object shown in Figure 5-1 are misleading and should not be relied upon. The donut model (what Ken Auer calls the soccer ball model because the dividing lines resemble the seams of a soccer ball) perpetuates the classic ideas of passive data and active procedures and better describes a COBOL program than an object. The animated data entity model also perpetuates the outdated separation of data and procedures and compounds that error by focusing on the distribution of data items ”attributes ”across a group of objects. Procedures are appended to the objects containing the distributed attributes. This data-driven approach to object modeling is quite popular but is not consistent with object thinking.

click to expand
Figure 5-1: Three popular graphical object models: soccer ball, animated data entity, and CRC card (left to right).

Another, and essential, way that objects differ from the modules at the heart of traditional software decomposition is that an object represents something naturally occurring in the problem domain, while a module represents a logical aggregation of elements appearing in the solution space. This is another reason why the foregoing graphical depictions are misleading and should be avoided. All three diagrams depict what is to be built instead of what is to be   modeled . Object discovery and specification must be domain driven!

Note  

What of middleware, networking protocols, persistence mechanisms (databases), and legacy systems? Are these part of the problem space, the domain, or part of the implementation space? The answer, as unhelpful as it might be, is that they can be either. You discover which they are by initially thinking of them as another domain object. Name them, and assign responsibilities to them. As you continue with your development, see whether the objects retain those responsibilities or whether all of the behaviors are reassigned to other objects. If the objects retain responsibilities, they are part of the problem space; if not, they are simply another solution space mechanism. For example, you might have a database object with the responsibilities of enabling object persistence, scheduling access to stored objects, enforcing object integrity constraints, providing backup services, and managing update transactions. It s quite conceivable that all of these responsibilities could be  transferred to other objects, leaving the database without a role. (See Chapter 10 for further discussion of objects and persistence.)

Responsibility

Responsibility means a service that an object has agreed (or been assigned) to perform. Objects are charged with performing specific tasks. Each task is a responsibility. The term responsibility is used to aid us in discovering who (which object) should be charged with a task without the need to think about the object s structure. (We should not know its structure.) Responsibilities are characterized from the point of view of a potential client of a service. I ask, Who would I reasonably ask for this service? and my answer determines which object becomes responsible for satisfying that type of service request.

Note  

XP practitioners talk of test-driven development and story-driven development. Both are instances of domain-driven development because both assume that tests and stories reflect the domain and the input of the on-site customer. There is an aspect to testing that is not directly reflective of the domain since some tests are directed to determining whether it was done right as well as it does what is wanted. The former set of tests determines the congruency of a particular solution with the dictates and constraints of the solution space.

The usual definition of an attribute is a characteristic of an entity, the value of which must be remembered by the system. In a data-driven approach, the attributes of an object are discovered first and then responsibilities are meted out as a function of which object holds which data. A behavioral approach mandates the assignment of responsibilities first. Only when you are satisfied with the distribution of responsibilities among your objects are you ready to  make a decision about what they need to know to fulfill those responsibilities and which parts of that knowledge they need to keep as part of their structure ”in instance variables or attributes. This is the single biggest difference in definition between data-driven and behavior-driven or responsibility-driven approaches to objects.

Responsibilities are not functions, although there is a superficial resemblance. Perhaps the easiest way to differentiate between a responsibility and a function is to remember that the former reflects expectations in the domain ”the problem space ”while the latter reflects an implementation detail in the solution space ”the computer program. Recall the applicability graph, Figure 3-2, and its division of the world of software into two realms. Use responsibilities when discussing anything in the realm defined by the upper left half of the graph and function for things far to the left and bottom of the graph.

An object might perform any of four basic types of services. (For a more detailed exploration of types of services an object might perform, see Rebecca Wirfs-Brock and Allan McKean s book Object Design: Roles, Responsibilities, and Collaborations .)

Maintain and supply on request one or more units of information.

The information (everything being an object) is an object such as a string or a character or a number, the value of which conveys meaning to an observer or a user . A person object might agree to provide its identification, which means it  agrees to provide you with a string, the value of which you would recognize as a unique identifier.

A more complex example would be an object, a product perhaps, that agrees to provide you with its description. In response to your request, this time the object gives you a description object . A description object can be thought of as a collection of labels and associated values called a value holder ( essentially the same thing as a Dictionary in Smalltalk or a Map in Java). (See Figure 5-2.) You then ask the description object for a particular value associated with a specific label.

click to expand
Figure 5-2: A value holder, a simple two-dimensional array with the contents of the first column restricted to labels and the contents of the second restricted to data objects such as strings, numbers, dates, and characters.

Perform a computational task.

This is a straightforward responsibility as long as you remember that computations must be performed by an object on itself. A number object, for example, can add itself to another number, [1] but it would be inappropriate for a calculator object to add two numbers together. A calculator is a kind of control object and should be avoided, both because it violates the object paradigm and because it  would have to be a very large and complicated object, another trait to be  avoided.

Note  

A calculator object might still exist in your solution ”but as a fa §ade for the community of objects doing the calculating rather than the single object doing the math. Fa §ades are quite useful because they provide a single point of reference for other objects seeking services. They can also provide a simple set of services, all or most of which are parsed and passed to objects behind the fa §ade for actual implementation.

This notion can be generalized so that a mathematical expression, which is a specific kind of computation, should be responsible for evaluating or solving itself. An abstraction of this service is a class of objects named SelfEvaluatingRule . This class will be discussed in detail in Chapter 10.

Report on or update the state of the object.

Sometimes an object will change its internal nature (its state ), and on those occasions it might be expected to report that change to others. For example, a seat object might have the state of empty, reserved, or occupied. When it changes from one state to another, potentially many other objects ”potential users of the seat ”would need to be notified of the change in state. The object should not, however, know anything about potential clients and should only indirectly keep a list of those objects that might want to be notified of state changes.

Every object should have the ability to accept registrations for event notification, even if many will never take advantage of that capability. Each registration is, of course, itself an object. Registration objects are accepted by a Dispatcher object, as illustrated in Figure 5-3. A registration message ( please register me for event X) would be sent by an object interested in one of the states you might find yourself in and of which you have indicated a willingness to notify others. The potential client of an event notification will send your eventDispatcher object a registration object (see Figure 5-4) and ask your eventDispatcher to add that object to the queue associated with the event of interest. The registration has an internal structure consisting of the name of the object sending the registration and the message that the client object wants sent to it when the event occurs. The dispatcherObject stores the registration objects it receives in notification queues associated with each potential event. When the state change occurs (an event is generated), each registration object in the appropriate queue is told to send its message to its recipient.

click to expand
Figure 5-3: A Dispatcher object. Events to be dispatched are located in the first column, whereas the second column is a queue of event registration requests .
click to expand
Figure 5-4: A registration request consists of a simple dyad ”who is to be notified (an object) and the means of notification (a message).

Coordinate other objects.

The key here is to maintain the blind coordination principle. Examples might be a dispatcher that routes messages without any knowledge of why the messages are being sent or a queue that coordinates the sending of a group of messages and hence the activation of the objects receiving those messages but does so only because its nature is to maintain order among a collection of objects and release them from the queue at appropriate times.

The same dispatching objects ( Dispatcher and Registration ) are used to effect the blind coordination desired. Individual objects can coordinate with one another by exchanging registrations, and group behavior can be effected by allowing a dispatcher to have global visibility ”again, like the traffic signal.

The traffic signal example lacks any obvious event registration and dispatching, but they are there nevertheless. Registration occurs when the driver of a vehicle or a pedestrian at the crosswalk gazes at the traffic light or walk signal. Dispatching occurs when the light from the signal is broadcast to all who can see.

Message

A message is a formal communication sent by one object to another requesting a service. A message can be imperative, informational, or interrogatory in nature.

An imperative message directs an object to make some change to itself. No response is expected or required. The object is assumed to have made the appropriate change. (It s quite possible, of course, for an object to offer confirmation of an imperative message by returning some specific kind of object, but care should be taken that this capability not be misused to create controller objects.)

An informational message is similar to an imperative in the sense that no response is expected. It differs from an imperative in that there is also no expectation that the receiving object will do anything at all in response to the message.

An interrogatory message is any request for a service. The object always returns an object (a typed value or a signal/interrupt in most popular programming languages) that encapsulates (embodies) the result requested . The returned object can be simple (a character or a string) or arbitrarily complex.

Messages frequently take the following form:

 ReceiverSelector(Arguments)   returnedObjectorReceiver.Selector (Arguments):ReturnedObject. 

where

  • Receiver identifies who is being sent the message. It might be a specific named object (the object Sara ), a generic object ( aPerson ), or the name of a place where an object resides (a variable). In the case of a variable, the object in residence actually receives and responds to the message.

  • Selector identifies the semantics of the message, the essence of the request. Selectors can be simple symbols (such as the mathematical operators, +, *, /, and so forth) or descriptive phrases ( nextObjectInLinePlease ).

  • (Arguments) are objects sent along with the message and are optional. They re optional in the sense that some selectors do not require an object, but if a message signature indicates that an argument is required, it is mandatory. Arguments are used when the receiver of a request for service also expects the requester to provide some of the information needed to perform that service.

  • returnedObject is an arbitrarily complex object containing or representing the result created as a consequence of receiving the message. In the case of imperative and declarative messages, the object returned is self, that is, the object that received the message is indicating it s still available to you (usually by keeping an active pointer). In Java, and most popular languages, imperative and declarative messages are void functions. No return is expected. If the object changed in any way as a result of receiving the message, the self that is returned is the newly constituted self.

What happens when you send a message and get back the wrong object, do not get a response at all, or get an explicit error message instead of the expected object? You figure out what went wrong, determine how the object should behave in such situations, write tests for that behavior, and code a new version of your object. The real question, however, is, Who handles errors? The answer is to make error handling an object responsibility ”one shared by every object ”and to let each object respond to those error conditions that it can resolve by using the knowledge it already has in its possession regarding itself and what it is trying to accomplish.

Interface (Protocol)

The collection of messages that an object responds to and the state changes it indicates it will accept registrations for constitute its interface . The term protocol is usually reserved for that portion of the interface listing the messages that the object is willing to respond to. A protocol might include messages of the sort , What state are you in? or Are you in state X? but those messages should not be confused with the innate ability of every object to notify others of a change in its state. The list of states and the mechanism for event registration and dispatching are quite separate from the message protocol. The syntax of each message in the protocol is specified and is considered the message signature . Where arguments are expected along with the message, the signature specifies the class (or type in some languages) of the object expected. The class (or type) of object returned in response to the message is also part of the signature.

The preceding terms are considered essential because they embody everything you need to know about objects to successfully decompose a domain, identify and distribute responsibilities among a collection of objects, create a taxonomy of objects, and even create scripts to guide objects in the completion of specific tasks.

These terms do not provide sufficient definition for those charged with actually implementing software objects. They provide a specification only. Implementation requires concepts associated with the solution space ”a programming language and an implementation platform. Also required is vocabulary for describing the internals of objects. A supporting vocabulary addresses these needs.

[1] In this example, the second number does assume a kind of passive role, being subsumed by the active number doing the calculation. But either number could have been the active one, so the principle of objects acting only on themselves is preserved.




Microsoft Object Thinking
Object Thinking (DV-Microsoft Professional)
ISBN: 0735619654
EAN: 2147483647
Year: 2004
Pages: 88
Authors: David West

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net