继续

lopper

来自: lopper(FREE TIME) 组长 2010-03-18 21:32:16

×
加入小组后即可参加投票
  • lopper

    lopper (FREE TIME) 组长 楼主 2010-04-07 22:41:39

    When you see the substitution principle it’s easy to feel like that’s 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 functions to the interface of a derived class. With inspection both cases should be reasonably obvious. 当你在考虑这种替换原理的时候会发现这是一种很容易会产生的解决问题的想法,并且如果你能够计算得当的话,它将相当的完美。但是你也会发现有时候你很明显的必须去给衍生类增加新的方法。通过观察你会发现两种模式都很是很合理的。 页48 Interchangeable objects with polymorphism 多态的可互换对象 Inheritance usually ends up creating a family of classes, all based on the same uniform interface. We express this with an inverted tree diagram:5 继承最终会形成一个像大家庭一样的类群,它们都继承了同一组接口。我们用一棵反转的树形图来说明它们:5 One of the most important things you do with such a family of classes is to treat an object of a derived class as an object of the base class. This is important because it means you can write a single piece of code that ignores the specific details of type and talks just to the base class. That code is then decoupled from type-specific information, and thus is simpler to write and easier to understand . And, if a new type-a Triangle, for example- is added through inheritance , the code you write will work just as well for the new type of Shape as it did on the existing types. Thus the program is extensible. 你对这个家族类能做的最重要的一件事就是把衍生类当作一个基类去对待。这样做的重要性在于你可以不用针对衍生类的特殊细节去写额外的处理代码,只要对基类负责就可以了。这些代码是从一些个性信息中分离出来的,所以它更容易书写而且易于理解。打个比方说一个类型三角形,通过继承被增加,你以前书写的代码同样适合与这种新类型,这就是程序的可扩展性。 Consider the above example. If you write a function in Java: 考虑上面的例子,如果你用Java写了以下代码: This function speaks to any Shape, so it is independent of specific type of object it’s drawing and erasing. If in some other program we use the doStuff() function: 这个函数可以针对任何形状类,所以它是独立与特殊类型对象的填充和擦除动作。如果我们在其他程序中使用doStuff()函数: 5 This uses the Unified Notation, Which will primarily be used in this book. 5这里使用了统一的表示方法,是这本书主要的表示方法。 页49 The calls to doStuff() automatically work right, regardless of the exact type of the object. This is actually a pretty amazing trick. Consider the line: 对于doStuff()的调用会自动调整,不用去考虑特殊的对象。 这的确是一种很好的方法,考虑下面这行: What’s happening here is that a Circle handle is being passed into a function that’s expecting a Shape handle. Since a Circle is a Shape it can be treated as one by doStuff(). That is, any message that doStuff() can send to a Shape, a Circle can accept. So it is a completely safe and logical thing to do. 到底发生了什么,一个圆句柄被这个函数接受因为它是一个形状句柄。因为圆又是一个形状,所以可以被doStuff()处理。任何一个doStuff()可以对形状发送的消息,圆形状同样可以接受,这是完全安全和合理的。 We call this process of treating a derived type as though it were its base type upcasting. The name cast is used in the sense of casting into a mold and up comes from the way the inheritance diagram is typically arranged, with the base type at the top and derived class fanning out downward. Thus, casting to a base type is moving up the inheritance diagram: upcasting. 我们把这种行为叫做对于衍生类的处理,以为它是对于基类的向上造型(upcasting)。这个名字造型(cast)被用于说明按照某个模型进行塑行,并且来自于我们上面的继承图所布置的,基类在上,衍生类按照扇形向下发散。所以,造型就是沿着继承图朝着基类向上:向上造型。 As object-oriented program contains some upcasting somewhere, because that’s how you decouple yourself from knowing about the exact type you’re working with. Look at the code in doStuff(): 因为面向对象编程包含某些向上造型的地方,这样你就可以不用按照特定类型的工作方式来编程,做到了去耦。看一下doStuff()的代码: Notice that it doesn’t say “If you’re a Circle, do this , if you’re a Square, do that, etc. ” If you write that kind of code, which checks for all the possible types a Shape. Can actually be, it’s messy and you need to change it every time you add a new kind of Shape. Here, you just say ”you’re a sharp , I know you can erase() yourself , do it and take care of the details correctly.” 我们看到这里没有说“如果你是一个圆,做这个,如果你是一个方,做这个等等”。如果你写了这种代码,将需要去检查所有可能的形状。这样将使程序非常混乱而且一旦增加了一个新的类型你就需要去修改你的程序。而现在你只需要去说“你是个形状,你可以擦除你自己,去做这个行为,保证细节合理。就可以了。” Dynamic binding 动态绑定 What’s amazing about the code in doStuff() is that somehow the right thing happens. Calling draw() for Circle causes different code to be executed than when calling draw() for a Square or a Line, but when the draw() message is sent to an anonymous Shape, the correct behavior occurs based on the actual type that the Shape handle happens to be connected to. This is amazing because when the Java compiler is compiling the code for doStuff(), it cannot know exactly what types it is dealing with. So ordinarily, you’d expect it to end up calling the version of erase() for Shape, and draw() for Shape and not for the specific Circle, Square, or Line. And yet the right thing happens. Here’s how it works. doStuff()的代码是令人惊喜的,因为某种意义上做了一些正确的工作。对于调用Circle调用draw()方法与对Square或者Line调用draw()方法会导致执行不同的代码,但是当对一个匿名形状发送draw()消息的时候,正确的方法被作用于一个实际的形状对象,这个实际的对象会被恰好关联于形状句柄。你只需要去调用对于Shape的erase()和draw(), 而不是对于具体Circle, Square, 或Line。把细节留给编译器和底层去处理。正确的事情就会发生。 When you send a message to an object even though you don’t know what specific type it is, and the right thing happens, that’s called polymorphism. The process used by object-oriented programming languages to implement polymorphism is called dynamic binding. The compiler and run-time system handle the details; all you need to know is that it happens and more importantly how to design with it. 当你对一个不确定具体类型的对象发送一个消息的时候,正确的工作被完成了,我们把发生这种情况的原因叫做多态性。被面向对象编程语言所实现的多态性被称作动态绑定。编译器和运行时系统完成了细节工作,你只要知道这种情况的存在并且做好设计工作就可以了。 Some languages require you to use a special keyword to enable dynamic binding. In C++ this keyword is virtual. In Java , you never need to remember to add a keyword because functions are automatically dynamically bound. So you can always expect that when you send a message to an object, the object will do the right things, even when upcasting is involved. 某些语言需要一个特殊的关键词去激活动态绑定,C++里的关键词是virtual。但是在Java语言里我们不需要去记住一个额外的关键词因为函数被自动动态绑定。所以我们可以期望当我们发送一个消息,正确的工作会发生在对象身上,即使是复杂的向上造型。 页50 Abstract base classes and interfaces 基于类和接口的抽象 Often in a design, you want the base class to present only an interface for its derived classes. That is, you don’t want anyone to actually create an object of the base class, only to upcast to it so that its interface can be used . This is accomplished by making that class abstract using the abstract keyword. If anyone tries to make an object of an abstract class, the complier prevents them. This is a tool to enforce a particular design. 经常在设计过程中,你想要基础类只把接口提供给衍生类。意思就是你只是希望去创建一个类去实现基类的接口,但是这个对象却不是基类。这个工作通常是通过创建一个抽象类来完成的,使用abstract关键词。如果有人想要创建一个抽象类的对象,编译器就会阻止它。这是一个强制去进行特殊设计的工具。 You can also use the abstract keyword to describe a method that hasn’t been implemented yet-as a stub indicating “here is an interface function for all types inherited from this class, but at this point I don’t have any implementation for it.” An abstract method may be created only inside an abstract class. When the class is inherited, that method must be implemented, or the inherited class becomes abstract as well. Creating an abstract method allows you to put a method in an interface without being forced to provide a possibly meaningless body of code for that method. 你也可以使用abstract关键词去描述一个方法没有被实现。像是一个存根上面写着“这是一个接口函数为所有继承这个类的衍生类,但是在这种情况下,没有任何实现”。一个抽象方法只可能被创建在一个抽象类中。当这个类被继承,这个方法也被实现,或者继承的类也变成了抽象类。创建一个抽象方法允许你去把一个方法放到一个接口中而不需要去强制提供可能没有任何意义的实现。 The interface keyword takes the concept of an abstract class one step further by preventing any function definitions at all. The interface is a very useful and commonly-used tool, as it provides the perfect separation of interface and implementation. In addition, you can combine many interfaces together, if you wish . (You cannot inherit from more than one regular class or abstract class.) Interface 关键词表达了一种概念就是不允许任何方法定义,这比起抽象类来更进了一步。接口是一个非常有用并且被广泛使用的工具,因为它提供了一个完美的对于接口和实现的分离,你可以按照你的想法把很多接口合并,(但是你不能把多个类或者抽象类合并)。 Object landscapes and lifetimes 对象景观 和生命周期 Technically, OOP is just about abstract data typing, inheritance and polymorphism, but other issues can be at least as important . The remainder of this section will cover these issues. 从技术的角度上来看OOP其实就是对于数据类型的抽象,继承和多态,但是其他的问题也很重要,我会在下面的章节里提到这些问题。 One of the most important factors is the way objects are created and destroyed. Where is the data for an object and how is the lifetime of the object controlled? There are different philosophies at work here. C++ takes the approach that control of efficiency is the most important issue, so it gives the programmer a choice. For maximum run-time speed, the storage and lifetime can be determined while the program is being written , by placing the objects on the stack (these are sometimes called automatic or scoped variables) or in the static storage area. This places a priority on the speed of storage allocation and release, and control of these can be very valuable in some situations. However , you sacrifice flexibility because you must know the exact quantity, lifetime and type of objects while you’re writing the program. If you are trying to solve a more general problem such as computer-aided design, warehouse management or air-traffic control, this is too restrictive. 有一个重要的因素关于对象的创建和销毁。对象的数据被放在哪里,它的生命周期如何?这里有不同的哲学供之。C++使用了效率比较高的方法,所以这是程序员的一个选择。最快的运行速度,存储空间和生命周期可以被程序员在编写程序时指定。通过把对象放在栈上(通常被称作自动或者域变量)或者在静态存储区。这样做会有一个很快的内存分配和释放的速度,并且能得到有效的控制。可是你牺牲了灵活性,你必须去知道准确的数量,生命周期和对象类型当你在写程序的时候。如果你试图去解决一个大体的问题,如程序辅助设计或者仓储管理或者航空客运控制,这样做是很受限的。 The second approach is to create objects dynamically in a pool of memory called the heap. In this approach you don’t know until run time how many objects you need , what their lifetime is or what their exact type is. Those are determined at the spur of the moment while the program is running . If you need a new object, you simply make it on the heap at the point that you need it. Because the storage is managed dynamically, at run time, the amount of time required to allocate storage on the heap is significantly longer than the time to create storage on the stack. (Creating storage on the stack is often a single assembly instruction to move the stack pointer down , and another to move it back up.) The dynamic approach makes the generally logical assumption that objects tend to be complicated, so the extra overhead of finding storage and releasing that storage will not have an important impact on the creation of an object. In addition , the greater flexibility is essential to solve the general programming problem. 另一种方法是在内存空间(heap堆)中动态的创建对象。在这种模式下你不知道有多少对象,它们的生存周期或者它们的准确类型,直到运行时。这些直到运行的那一刻才被决定。如果你需要一个新的对象,那么可以在堆上创建它。因为存储空间被动态管理,所以我们需要去在堆上分配内存明显要比在栈上耗费更长的时间(在栈上创建空间通常只需要一条汇编命令去排入或弹出)。动态性使得对象可以更复杂,所以我们不在关注于内存的寻找与释放,而是更大的灵活性使得程序开发变的容易。

你的回应

回应请先 , 或 注册

3 人聚集在这个小组
↑回顶部