UML

前言

在阅读一些编程类的书籍时,常常会看到一些图形,这些图形可以形象的表示对象类型之间的关系,这里通过对TIJ中的所有此类的图形进行整理,总结,从而系统的明白这个图形的阅读与应用。在TIJ中共有21处应用了这个关系图形UML(Unified Modelling Language,统一建模语言)。

1.节选自TIJP27(p3)

在这里插入图片描述

Light lt = new Light();
lt.on();

  • Each object can satisfy only certain requests. The requests you can make of an object are defined by its interface, and the type is what determines the interface. A simple example might be a representation of a light bulb.
  • The interface determines the requests that you can make for a particular object. However there must be code somewhere to satisfy that request. This, along with the hidden data, comprises the implementation. From a procedural programming standpoint, it’s not that complicated. A type has a method associated with each possible request, and when you make a particular request to an object, that method is called. This process is usually summarized by saying that you “send a message” (make a request) to an object, and the object figures out what to do with that message (it executes code).
  • Here, the name of the type/class is Light, the name of this particular Light object is lt, and the requests that you can make of a Light object are to turn it on, turn it off, make it brighter, or make it dimmer. You create a Light object by defining a “reference” (lt) for that object and calling new to request a new object of that type. To send a message to the object, you state the name of the object and connect it to the message request with a period (dot). From the standpoint of the user of a pridefined class, that’s pretty much all there is to programming with objects.
  • The preceding diagram follows the format of the Unified Modeling Language (UML). Each class is represented by a box, with the type name in the top portion of the box, any data members that you care to describe in the middle portion of the box, and the methods (the functions that belong to this object, which receive any messages you send to that object) in the bottom portion of the box. Often, only the name of the class and the public methods are shown in UML design diagrams, so the middle portion is not shown, as in this case. If you’re interested only in the class name, then the bottom portion doesn’t need to be shown, either.

2.节选自TIJP32(p6)

This UML diagram indicates composition with the filled diamond, which states there is on car. I will typically use a simpler form: just a line, without the diamond, to indicate an association.(This is usually enough detal for most diagrams, and you don’t need to get specific about whether you’re using aggregation or composition.)
在这里插入图片描述

  • Code reuse is one of the greatest advantages that object-oriented programming languages provide.
  • The simplest way to reuse a class is to just use an object of that class directly, but you can also place an object of that class inside a new class. We call this “creating a member object.” Your new class can be made up of any number and type of other objects, in any combination that you need to achieve the functionality desired in your new class. Because you are composing a new class from existing classes, this concept is called composition (if the Composition happens dynamically, it’s usually called aggregation). Composition is often referred to as a “has-a” relationship, as in “A car has an engine.”
  • Composition comes with a great deal of flexibility. The member objects of your new class are typically private, making them inaccessible to the client programmers who are using the class. This allows you to change those members without disturbing existing client code. You can also change the member objects at run time, to dynamically change the behavior of your program. Inheritance, which is described next, does not have this flexibility since the compiler must place compile-time restrictions on classes created wth inheritance.
  • Because inheritance is so important in object-oriented programming, it is often highly emphasized, and the new programmer can get the idea that inheritance should be used everywhere. This can result in awkward and overly complicated designs. Instead, you should first look to composition when creating new classes, since it is simpler and more flexible. If you take this approach, your designs will be cleaner. Once you’ve had some experience, it will be reasonably obvious when you need inheritance.

3.节选自TIJP33(p6)

The arrow in this UML diagram points from the derived class to the base class. As you will see, there is commonly more than one derived class.
在这里插入图片描述

  • By itself, the idea of an object is a convenient tool. It allows you to package data and functionality together by concept, by concept, so you can represent an appropriate problem-space idea rather than being forced to use the idioms of the underlying machine. Theseconcepts are expressed as fundamental units in the programming language by using the class keyword.
  • It sees a pity, however, to go to all the trouble to create a class and then be forced to create a brand new one that might have similar functionality. It’s nicer if we can take the existing class, clone it, and then make additions and modifications to the clone. This is effectively whta you get with inheritance, with the exception that if the original class (called the base class or superclass or parent class) is changed, the modified “clone” (called the derived class or inherited class or subclass or child class) also reflects those changes.
  • A type does more than describe the constraints on a set of objects; it also has a relationship with other types. Two types can have characteristics and behaviors in common, but one type may contain more characteristics than another and may also handle more messages (or handle them differently). Inheritance expresses thes similarity between types by using the concept of base types and derived types. A base type contains all of the characteristics and behaviors that are shared among the types derived from it. You create a base type to represent the core of your ideas about some objects in your system. From the base type, you derive other types to express the different ways that this core can be realized.

4.节选自TIJP34(p7)

在这里插入图片描述

  • A inheritance example is the classic “shape” example, perhaps used in a computer-aided design system or game simulation. The base type is “shape,” and each shape has a size, a color, a position, and so on. Each shape can be drawn, erased, moved, colored, etc. From this, specific types of shapes are derived (inherited)–circle, square, triangle and so on–each of which may have additional characteristics and behaviors. Cerain shapes can be flipped, for example. Some behaviors may be different, such as when you want to calculate the area of a shape. The type hierarchy embodies both the similarities and differences between the shapes.
  • Casting the solution in the same terms as the problem is very useful because you don’t need a lot of intermediate models to get from a description of the problem to a description of the solution. With objects, the type hierarchy is the primary model, so you go directly from the description of the system in the real world to the description of the system in code. Indeed, one of the difficulties people have with object-ornted design is that it’s too simple to get from the beginning to the end. A mind trained to look for complex solutions can initially be stumped by this simplicity.
  • When you inherit from an existing type, you create a new type. This new type contains not only all the members of the existing type (although the private ones are hidden away an inaccessible), but more importantly it duplicates the interface of the base class. That is, all the messages you can send to objects of the base class you can also send to objects of the derived class. Since we know the type of a class by the messages we can send to it, this means that the derived class is the same type as the base class. In the previous example, “A circle is a shape.” This type equivalence via inheritance is one of the fundamental gateways in understanding the meaning of object-oriented programming.

5.节选自TIJP36(p7)

在这里插入图片描述

  • Since both the base class and derived class have the same fundamental interface, there must be some implementatin to go along with that interface. That is, there must be some code to excute when an object receives a particular message. If you simply inherit a class and don’t do anything else, the methods from the base-class interface come right along into the derived class. That means objects of the derived class have not only the same type, they also have the same behavior, which isn’t particularly interesing.
  • You have two ways to differentiate your new derived class from the original base class. The first is quite straightforward: You simply add brand new methods to the derived lass. These new methods are not part of the base-class inter face. This means that the base class simply didn’t do as much as you wanted it to, so you added more methods. This simple and primitive use for inheritance is, at times, the perfect solution to your problem. However, you should look closely for the possibility that your base class might also need these additional methods. This process of discovery and iteration of your design happens regularly in object-oriented programming.
  • Although inheritance may sometimes imply (especially in Java, where the keyword for inheritance is extends) that you are going to add new methods to the interface, that’s not necessarily true. The second and more important way to differentiate your new class is to change the behavior of an exising base-class method. This is referred to as overriding that method.
  • To override a method, you simply create a new definition for the method in the derived class. You’re saying, “I’m using the same interface method here, but I want it to do something different for my new type.”

6.节选自TIJP38(p8)

在这里插入图片描述
Is-a vs. is-like-a relationships

  • There’s a certain debate that can occur ablout inheritance: Should inheritance override only base-class methods (and not add new methods that aren’t in the base class)? This would mean that the derived class is exactly the same type as the base class since it has exactly the same interface. As a result, you can exactly subsitute an object of the derived class for an object of the base calss. This can be thought of as pure subsitution, and it’s often referred to as the substitution principle. In a sense, this is the ideal way to treat inheritance. We often refer to the relationship between the base class and derived classes in this case as an is-a relationship, because you can say, “A circle is a shape.” A test for inheritance is to determine whether you can state the is-a relationship about the classes and haveit make sense.
  • There are times when you must add new interface elements to a derived type, thus extending the interface. The new type can still be substituted for the base type, but the substitution isn’t perfect because your new methods are not accessible from the base type. This can be described as an is-like-a relationship (my term). The new type has the interface of the old type but it also contains other methods, so you can’t really say it’s exactly the same For example, consider an air conditioner. Suppose your house is wired with all the controls for cooling; that is, ithas an interface that allows you to control cooling. Imagine that the air conditioner breaks down and you replace it with a heat pump, which can both heat and cool. The heat pump is-like-an air conditioner, but it can do more. Because the control system of your house is designed only to control cooling, it is restricted to communication with the cooling part of the new object. The interface of the new object has been extended, and the existing system doesn’t know about anything except the original interface.
  • Of course, once you see this design it becomes clear that the base class “cooling system” is not general enough, and should be renamed to “temperature control system” so that it can also include heating–at which point the substitution principle will work. However, this diagram is an example of whta can happen with design in the real world.
  • When you see the substitution principle it’s easy to feel like this approach (pure substitution) is the only way to do things, and in fact it is nice if your design works out that way. But you’ll find that there are times when it’s equally clear that you must add new methods to the interface of a derived class. With inspection both cases should be reasonably obvious.

7.节选自TIJP40(p9)

在这里插入图片描述
Interchangeable objects with polymorphism

  • When dealing with type hierarchies, you often want to treat an object not as the specific type that it is, but instead as its base type. This allows you to write code that doesn’t depend on specific types. In the shape example, methods manipulate generic shapes, unconcerned about whether they’re circles,squares, triangles, or some shape that hasn’t even been defined yet. All shapes can be drawn, erased, and moved, so these methods simply send a message to a shape object; they don’t worry about how the object copes with the message.
  • Such code is unaffected by the addition of new types, and adding new types is the most common way to extend an object-oriented program to handle new situations. For example, you can derive a new subtype of shape called pentagon without modifying the methods that deal only with generic shapes. This ability to easily extend a design by deriving new subtypes is one of the essential ways to encapsulate change. This greatly improves designs while reducingg the cost of software maintenance.
  • There’s a problem, however, with attempting to treat derived-ype objects as their generic base types (circles as shapes, bicycles as vehicles as veehicles, cormorants as birds, etc.). If a method is going to tell a generic shape to draw itself, or a generic wehicle to steer, or a generic bird to move, the compiler cannot know at compile time precisely what piece of code will be executed. That’s the whole point–when the message is sent, the programmer doesn’t want to know what piece of code will be executed; the draw method can be applied equally to a circle, a square, or a triangle, and the object will execute the proper code depending on its specific type.
  • If you don’t have to know what piece of code will be executed, then when you add a new subtype, the code it executes can be different without requiring changes to the method that calls it. Therefore, thecompiler cannot know precisely what piece of code is executed, so what does it do? For example, in the following diagram the BirdController object just works with generic Bird objects and does not know what exact type they are. This is conenient from BirdController’s perspective because it doesn’t have to write special code to determine the exact type of Bird it’s working with or that Bird’s behavior. So how does it happen that, when move() is called while ignoring the specific type of Bird, the right behavior will occur (a Goose walks, flies, or swims, and Penguin alks or swims)?
  • The answer is the primary twist in object-oriented programmming: The compiler cannot mmake a function call in thettraditional sense. The function call generated by a non-OOP compiler causes what is called early binding, a terrm you may not have heard before because you’ve never thought about itany other way. It means the compiler generats a call to a specific function name, and the runtime system resolves this call to the absolute address of the code to bee executed. In OOP, the program cannot determine the address of the code until run time, so some other scheme is necessary when a message is sent to a generic object.
  • To solve the problem, object-oriented languages use the concept of late binding. When you send a message to an object, the code being called isn’t determined until run time. The compiler does ensure that the method exists and performs type checking on the arguments and return value, but it doesn’t know the exact code to execute.
  • To perform late bingding, Java uses a special bit of code in lieu of the absolute call. This code calculates the address of the method body, using information stored in the object (this process is covered in great detail in the Polymorphism chapter). Thus, each object can behave differently according to the contents of that special bit of code. When you send a message to an object, the object actually does figure out what to do with that message.
  • In somelanguages you must explicitly state that you want a method to have the flexibility of late-binding properties (C++ uses the virtual keyword to do this). In these languages, by default, methods are not dynamically bound. In Java, dynamic binding is the default behavior and you don’t need to remember to add any extra keywords in order to get polymorphism.

8.节选自TIJP42(p10)

在这里插入图片描述

9.?节选自TIJP283

10.?节选自TIJP287

11.?节选自TIJP306

12.?节选自TIJP307(1)

13.?节选自TIJP307(2)

14.?节选自TIJP308

15.?节选自TIJP313

16.?节选自TIJP317

17.?节选自TIJP326

18.节选自TIJP439(p246)

在这里插入图片描述
Summary

  • Java provides a number of ways to hold objects:
  • 1.An array associates numerical indexes to objects. It holds objects of a known type so that you don’t have to cast the result when you’re looking up an object. It can be multidimensional, and it can hold primitives. However, its size cannot be changed once you create it.
  • 2.A Collection holds single elements, and a Map holds associated pairs. With Java generics, you specify the type of object to be held in the containers, so you can’t put the wrong type into a container and you don’t have to cast elements when you fetch them out of a container. Both Collections and Maps automatically resize themselves as you add more elements. A container won’t hold primitives, but autoboxing takes care of translating primitives back and forth to the wrapper types held in the container.
  • 3.Like an array, a List also associates numerical indexes to objects— thus, arrays and Lists are ordered containers.
  • 4.Use an ArrayList if you’re doing a lot of random accesses, but a LinkedList if you will be doing a lot of insertions and removals in the middle of the list.
  • 5.The behavior of Queues and stacks is provided via the LinkedList.
  • 6.A Map is a way to associate not integral values, but objects with other objects. HashMaps are designed for rapid access, whereas a TreeMap keeps its keys in sorted order, and thus is not as fast as a HashMap. A LinkedHashMap keeps its elements in insertion order, but provides rapid access with hashing.
  • 7.A Set only accepts one of each type of object. HashSets provide maximally fast lookups, whereas TreeSets keep the elements in sorted order. LinkedHashSets keep elements in insertion order.
  • 8.There’s no need to use the legacy classes Vector, Hashtable, and Stack in new code.
  • It’s helpful to look at a simplified diagram of the Java containers (without the abstract classes or legacy components). This only includes the interfaces and classes that you will encounter on a regular basis.

see picture

  • You’ll see that there are really only four basic container components—Map, List, Set, and Queue—and only two or three implementations of each one (the java.util.concurrent implementations of Queue are not included in this diagram). The containers that you will use most often have heavy black lines around them.
  • The dotted boxes represent interfaces, and the solid boxes are regular (concrete) classes. The dotted lines with hollow arrows indicate that a particular class is implementing an interface. The solid arrows show that a class can produce objects of the class the arrow is pointing to. For example, any Collection can produce an Iterator, and a List can produce a ListIterator (as well as an ordinary Iterator, since List is inherited from Collection).
  • Here’s an example that shows the difference in methods between the various classes. The actual code is from the Generics chapter; I’m just calling it here to produce the output. The output also shows the interfaces that are implemented in each class or interface:
//: holding/ContainerMethods.java 
import net.mindview.util.*; 
public class ContainerMethods {   
	public static void main(String[] args) { 
	    ContainerMethodDifferences.main(args);   
	}
} /* Output: (Sample) 
Collection: [add, addAll, clear, contains, containsAll, equals, hashCode, isEmpty, iterator, remove, removeAll, retainAll, size, toArray] 
Interfaces in Collection: [Iterable] 
Set extends Collection, adds: [] 
Interfaces in Set: [Collection] 
HashSet extends Set, adds: [] 
Interfaces in HashSet: [Set, Cloneable, Serializable] 
LinkedHashSet extends HashSet, adds: [] 
Interfaces in LinkedHashSet: [Set, Cloneable, Serializable] 
TreeSet extends Set, adds: [pollLast, navigableHeadSet, descendingIterator, lower, headSet, ceiling, pollFirst, subSet, navigableTailSet, comparator, first, floor, last, navigableSubSet, higher, tailSet] 
Interfaces in TreeSet: [NavigableSet, Cloneable, Serializable] 
List extends Collection, adds: [listIterator, indexOf, get, subList, set, lastIndexOf] 
Interfaces in List: [Collection] 
ArrayList extends List, adds: [ensureCapacity, trimToSize] 
Interfaces in ArrayList: [List, RandomAccess, Cloneable, Serializable] 
LinkedList extends List, adds: [pollLast, offer, descendingIterator, addFirst, peekLast, removeFirst, peekFirst, removeLast, getLast, pollFirst, pop, poll, addLast, removeFirstOccurrence, getFirst, element, peek, offerLast, push, offerFirst, removeLastOccurrence] 
Interfaces in LinkedList: [List, Deque, Cloneable, Serializable] 
Queue extends Collection, adds: [offer, element, peek, poll] 
Interfaces in Queue: [Collection] 
PriorityQueue extends Queue, adds: [comparator] 
Interfaces in PriorityQueue: [Serializable] 
Map: [clear, containsKey, containsValue, entrySet, equals, get, hashCode, isEmpty, keySet, put, putAll, remove, size, values] 
HashMap extends Map, adds: [] 
Interfaces in HashMap: [Map, Cloneable, Serializable] 
LinkedHashMap extends HashMap, adds: [] 
Interfaces in LinkedHashMap: [Map] 
SortedMap extends Map, adds: [subMap, comparator, firstKey, lastKey, headMap, tailMap] 
Interfaces in SortedMap: [Map] 
TreeMap extends Map, adds: [descendingEntrySet, subMap, pollLastEntry, lastKey, floorEntry, lastEntry, lowerKey, navigableHeadMap, navigableTailMap, descendingKeySet, tailMap, ceilingEntry, higherKey, pollFirstEntry, comparator, firstKey, floorKey, higherEntry, firstEntry, navigableSubMap, headMap, lowerEntry, ceilingKey] 
Interfaces in TreeMap: [NavigableMap, Cloneable, Serializable] 
*///:~ 
  • You can see that all Sets except TreeSet have exactly the same interface as Collection. List and Collection differ significantly, although List requires methods that are in Collection. On the other hand, the methods in the Queue interface stand alone; the Collection methods are not required to create a functioning Queue implementation. Finally, the only intersection between Map and Collection is the fact that a Map can produce Collections using the entrySet( ) and values( ) methods.
  • Notice the tagging interface java.util.RandomAccess, which is attached to ArrayList but not to LinkedList. This provides information for algorithms that might want to dynamically change their behavior depending on the use of a particular List.
  • It’s true that this organization is somewhat odd, as object-oriented hierarchies go. However, as you learn more about the containers in java.util (in particular, in the Containers in Depth chapter), you’ll see that there are more issues than just a slightly odd inheritance structure. Container libraries have always been difficult design problems—solving these problems involves satisfying a set of forces that often oppose each other. So you should be prepared for some compromises here and there.
  • Despite these issues, the Java containers are fundamental tools that you can use on a day-to-day basis to make your programs simpler, more powerful, and more effective. It might take you a little while to get comfortable with some aspects of the library, but I think you’ll find yourself rapidly acquiring and using the classes in this library.
  • Solutions to selected exercises can be found in the electronic document The Thinking in Java Annotated Solution Guide, available for sale from www.MindView.net.

19.?节选自TIJP554

20.?节选自TIJP792

21.?节选自TIJP961

未完待续。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值