Polymorphism - UML

Problem

How handle alternatives based on type? How to create pluggable software components?

Alternatives based on type - Conditional variation is a fundamental theme in programs. If a program is designed using if - then - else or case statement conditional logic, then if a new variation arises, it requires modification of the case logic - often in many places. This approach makes it difficult to easily extend a program with new variations because changes tend to be required in several places - wherever the conditional logic exists.

Pluggable software components - Viewing components in client - server relationships, how can you replace one server component with another, without affecting the client?

Solution

When related alternatives or behaviors vary by type (class), assign responsibility for the behavior - using polymorphic operations - to the types for which the behavior varies.

Corollary: Do not test for the type of an object and use conditional logic to perform varying alternatives based on type.

Examples NextGen Problem: How Support Third - Party Tax Calculators?

In the NextGen POS application, there are multiple external third - party tax calculators that must be supported (such as Tax - Master and Good - As - Gold Tax - Pro); the system needs to be able to integrate with different ones. Each tax calculator has a different interface, so there is similar but varying behavior to adapt to each of these external fixed interfaces or APIs. One product may support a raw TCP socket protocol, another may offer a SOAP interface, and a third may offer a Java RMI'interface.

What objects should be responsible for handling these varying external tax calculator interfaces?

Since the behavior of calculator adaptation varies by the type of calculator, by Polymorphism we should assign the responsibility for adaptation to different calculator (or calculator adapter) objects themselves, implemented with a polymorphic getTaxes operation.

These calculator adapter objects are not the external calculators, but rather, local software objects that represent the external calculators, or the adapter for the calculator. By sending a message to the local object, a call will ultimately be made on the external calculator in its native API.

Each getTaxesmethod takes the Sale object as a parameter, so that the calculator can analyze the sale. The implementation of each getTaxes method will be different: TaxMaster Adapter will adapt the request to the API of Tax - Master, and so on.

Figure 25.1 Polymorphism in adapting to different external tax calculators

UML - Notice the interface and interface realization notation in Figure.

Monopoly Problem: How to Design for Different Square Actions?

To review, when a player lands on the Go square, they receive $200. There's a different action for landing on the Income Tax square, and so forth. Notice thatthere is a different rule for different types of squares. Let's review the Polymorphism design principle:

When related alternatives or behaviors vary by type (class), assign responsibility for the behavior - using polymorphic operations - to the types for which the behavior varies. Corollary: Do not test for the type of an object and use conditional logic to perform varying alternatives based on type.

From the corollary, we know we should not design with case logic (a switch statement in Java or C#) as in the following pseudocode:

//bad design SWITCHONsquare.type
CASEGoSguare:playerreceives$200
CASEIncomeTaxSquare:player paystax
...

Rather, the principle advises us to create a polymorphic operation for each type for which the behavior varies. It varies for the types (classes) RegularSquare, GoSquare, and so on. What is the operation that varies? It's what happens when a player lands on a square. Thus, a good name for the polymorphic operation is landedOnor some variation. Therefore, by Polymorphism, we'll create a separate class for each kind of square that has a different landedOnresponsibility, and implement a landed Onmethod in each. Figure illustrates the static - view class design.

Figure 25.2 Applying Polymorphism to the Monopoly problem

Applying UML: Notice in Figure the use of the (abstract} keyword for the landedOnoperation.

Guideline: Unless there is a default behavior in the superclass, declare a polymorphic operation in the superclass to be {abstract}.

The remaining interesting problem is the dynamic design: How should the interaction diagrams evolve? What object should send the landedOnmessage to the square that a player lands on? Since a Player software object already knows its location square (the one it landed on), then by the principles of Low Coupling and by Expert, class Player is a good choice to send the message, as a Player already has visibility to the correct square.

Naturally, this message should be sent at the end of the takeTurnmethod. Please review the iteration - 1 takeTurndesign on to see our starting point. Figures illustrate the evolving dynamic design.

Figure 25.3 Applying Polymorphism

Applying UML:

  • Notice in Figures the informal approach to showing the polymorphic cases in separate diagrams when sketching UML. An alternative - especially when using a UML tool - is to use sdand ref frames.

  • Notice in Figure that the Player object is labeled 'p' so that in the landedOnmessage we can refer to that object in the parameter list. (You will see in Figure that it is useful for the Square to have parameter visibility to the Player.)

  • Notice in Figure that the Square object is labeled loc(short for 'location') and this is the same label as the return value variable in the getSquaremessage. This implies they are the same object.

Let's consider each of the polymorphic cases in terms of GRASP and the design issues:

  1. GoSquare:- See Figure By low representational gap, the Player should know its cash. Therefore, by Expert, it should be sent an addCashmessage. Thus the square needs visibility to the Player so it can send the message; consequently, the Player is passed as a parameter 'p' in the landedOn message to achieve parameter visibility.

  2. RegularSquare:- See Figure In this case, nothing happens. I've informally labeled the diagram to indicate this, though a UML note box could be used as well. In code, the body of this method will be empty - sometimes called a NO - OP (no operation) method. Note that to make the magic of polymorphism work, we need to use this approach to avoid special case logic.

  3. IncomeTaxSquare:- See Figure We need to calculate 10% of the player's net worth. By low representational gap and by Expert, who should know this? The Player. Thus the square asks for the player's worth, and then deducts the appropriate amount.

  4. GoTo JailSquare:- See Figure Simply, the Player's location must be changed. By Expert, it should receive aetLocationmessage. Probably, the GoTo JailSquarewill be initialized with an attribute referencing the JailSquare, so that it can pass this square as a parameter to the Player.

UML as Sketch: Notice in Figure that the vertical lifeline is drawn as a solid line, rather than the traditional dashed line. This is more convenient when hand sketching. Furthermore, UML 2 allows either format - although in any event conformance to correct UML is not so important when sketching, only that the participants understand each other.

Figure 25.4 TheGoSquarecase

Figure 25.5 The RegulorSquare case

Figure 25.6 The Income Tax Square case

Figure 25.7 TheGoToJailSquarecase

Improving the Coupling

As a small 00 design refinement, notice in Figure on for iteration - 1 that the Piece remembers the square location but the Player does not, and thus the Player must extract the location from the Piece (to send the getSquare message to the Board), and them re - assign the new location to the Piece. That's a weak design point, and in this iteration, when the Player must also send the landedOn message to its Square, it becomes even weaker. Why? What's wrong with it? Answer: Problems in coupling.

Clearly the Player needs to permanently know its own Square location object rather than the Piece, since the Player keeps collaborating with its Square. You should see this as a refactoring opportunity to improve coupling - when object A keeps needing the data in object B it implies either

  1. object A should hold that data, or
  2. object B should have the responsibility (by Expert) rather than object A.

Therefore, in iteration - 2 I've refined the design so that the Player rather than the Piece knows its square; this is reflected in both the DCD of Figure and the interaction diagram of Figure.

In fact, one can even question if the Piece is a useful object in the Design Model. In the real world, a little plastic piece sitting on the board is a useful proxy for a human, because we're big and go to the kitchen for cold beer! But in software, the Player object (being a tiny software blob) can fulfill the role of the Piece.

Discussion

Polymorphism is a fundamental principle in designing how a system is organized to handle similar variations. A design based on assigning responsibilities by Polymorphism can be easily extended to handle new variations. For example, adding a new calculator adapter class with its own polymorphic getTaxes method will have minor impact on the existing design.

Guideline: When to Design with Interfaces?

Polymorphism implies the presence of abstract superclasses or interfaces in most 00 languages. When should you consider using an interface? The general answer is to introduce one when you want to support polymorphism without being committed to a particular class hierarchy. If an abstract superclass AC is used without an interface, any new polymorphic solution must be a subclass of AC, which is very limiting in single - inheritance languages such as Java and C#.

As a rule - of - thumb, if there is a class hierarchy with an abstract superclass CI, consider making an interface II that corresponds to the public method signatures of CI, and then declare CI to implement the II interface. Then, even if there is no immediate motivation to avoid subclassing under CI for a new polymorphic solution, there is a flexible evolution point for unknown future cases.

Contraindications

Sometimes, developers design systems with interfaces and polymorphism for
speculative "future - proofing" against an unknown possible variation. If the variation point is definitely motivated by an immediate or very probable variability. then the effort of adding the flexibility through polymorphism is ofcourse rational. But critical evaluation is required, because it is not uncommon to see unnecessary effort being applied to future - proofing a design with polymorphism at variation points that in fact are improbable and will never actually arise. Be realistic about the true likelihood of variability before investing in increased flexibility.

Benefits

  • Extensions required for new variations are easy to add.
  • New implementations can be introduced without affecting clients.
  • Protected Variations
  • A number of popular GoF design patterns [GHJV95], which will be discussed in this book, rely on polymorphism, including Adapter, Command, Composite, Proxy, State, and Strategy.

Also Known As Similar To; Choosing Message, Don't Ask "What Kind?"


All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

UML Topics