johnson@m.cs.uiuc.edu (07/28/90)
I think that Paul Sander's B-tree abstraction is a pretty good reusable component. However, I think that Bill Ogden has a good point. Bill's basic point is that it is really more important to reuse specifications than it is to reuse implementations. Of course, we need to provide implementations for our specifications, but the user should think mostly of the specification and not the implementation. B-trees are an implementation, while almost-constant functions are specification. Further, I take the point of view held by the object-oriented community that it is not particular kinds of components that are important, but rather sets of components and their relationships. The advantage of concentrating on specification is that you end up with lots of components that share the same specification. When you learn one, you have learned something about all of them. Also, it becomes easier to replace one component with another because they will share specification. Thus, it becomes easier to write utilities that work with any of the related components. This is not all that relevant to Paul's original article, but I saw a soapbox, and couldn't resist getting on it. Ralph Johnson -- University of Illinois at Urbana-Champaign
rick@.tetrauk.UUCP (Rick Jones) (07/30/90)
In article <71800004@m.cs.uiuc.edu> johnson@m.cs.uiuc.edu (Ralph Johnson) writes: > >I think that Paul Sander's B-tree abstraction is a pretty good >reusable component. However, I think that Bill Ogden has a >good point. Bill's basic point is that it is really more >important to reuse specifications than it is to reuse >implementations. Of course, we need to provide implementations >for our specifications, but the user should think mostly of the >specification and not the implementation. B-trees are an >implementation, while almost-constant functions are specification. > >Further, I take the point of view held by the object-oriented >community that it is not particular kinds of components that are >important, but rather sets of components and their relationships. >The advantage of concentrating on specification is that you end >up with lots of components that share the same specification. >When you learn one, you have learned something about all of them. >Also, it becomes easier to replace one component with another >because they will share specification. Thus, it becomes easier >to write utilities that work with any of the related components. I see this as the key difference between the OO concept of re-use, and the procedural library approach. In both cases, software is certainly re-used, but the whole OO thing is really about specification and design. OO languages provide a way of moving from the design stage to the coding stage without the sort of traumatic paradigm shift which results in design information getting lost. Further, OO encourages _design_ re-use as much as code re-use. Abstract classes provide one means to do this, so that you can have an abstract structure (B-tree or whatever else you want), and different implementations inheriting from it. The program components which use the structure can use the abstract type, except where the structure is actually created, which is where the decision has to be made; this should only be at one place. If you decide to use a different implementation of the structure, you should only need to change about one line of code. The other important re-use issue is partial re-use. You have a component which is nearly what you want, but you need a few variations for your application. This is what inheritance can give you. This leads into a problem which I don't think is resolved, or even fully understood, regarding what you actually inherit. Is it an abstraction (i.e. a consistent interface) or an implementation (i.e. some useful chunks of code)? A statically type-checked language assumes and/or requires descendant classes to conform to their parents; but do you always want that? It sounds like a good case for dynamic type checking (Smalltalk and Objective-C users can afford to smirk here :-). [ Extended discussion of inheritance aborted - please pick it up if you're interested. ] Although you can take a very object-oriented approach to software with a standard procedural language, careful design, and good coding discipline, the concepts of abstract types and re-use by inheritance really require a true object-oriented language to make them work for you. I find it helps to think of programs as recipes rather than actual components. There will be a recipe in the library for a plain cake. If you want to write a recipe for a fruit cake, you just write "Proceed as for a plain cake, but add a handful of fruit in the final mixing" (this is not a recommendation for my cakes!). You "inherit" the plain cake recipe from the library, and produce a descendant recipe. This will create a different object, and it is the _objects_ which are the "components"; they are created at run-time and behave according to their class descriptions (i.e. the programs). You don't make a good fruit cake by buying a plain cake from the shop and then trying to stuff fruit into it when you get home. In summary, I believe it's better to treat software as detailed component designs, rather than actual components, and thus software re-use _is_ design and specification re-use. The major benefit of object-oriented languages, and the way of thinking which they (should) promote, is to bring the ideas of "design" and "code" closer together so that they can be treated as different levels of the same thing. -- Rick Jones You gotta stand for something Tetra Ltd. Maidenhead, Berks Or you'll fall for anything rick@tetrauk.uucp (...!ukc!tetrauk.uucp!rick) - John Cougar Mellencamp
johnson@m.cs.uiuc.edu (08/01/90)
Paul Sander writes: >I was under the impression that a major reason for inventing reusable >components was to reuse code, that is to recycle implementations. Certainly. However, it turns out that you get better code reuse when you think about standardizing specifications. Futher, you can't reuse code as often as you can reuse design. Paul and I seem to have a different definition of specification. A specification does not have to define a complete behavior for a component. In fact, the most useful specifications are incomplete. Most components that meet a particular specification will in fact meet much more detailed specifications as well. I agree that it isn't crucial that we have many implementations of specifications such as almost-constant-functions. However, it is a good idea to have many implementations of specifications such as displayable objects. It is important that all displayable objects conform to a standard interface so that programs can manipulate images without having to worry about their complexity. Like I said, >>Further, I take the point of view held by the object-oriented >>community that it is not particular kinds of components that are >>important, but rather sets of components and their relationships. >>The advantage of concentrating on specification is that you end >>up with lots of components that share the same specification. >>When you learn one, you have learned something about all of them. >>Also, it becomes easier to replace one component with another >>because they will share specification. Thus, it becomes easier >>to write utilities that work with any of the related components. Perhaps if you replace "specification" with "interface" then you will understand my point better. I believe that there is no real difference in the terms. I've been building object-oriented systems for a few years now. Inheritance is nice, and I don't want to do without it, but polymorphism is more important. Polymorphism depends on a set of different classes that all meet the same specification. >In practice while programming, it turns out that the interfaces of the >two objects may be the same, but the objects are hardly interchangeable, >since their semantics are different. They may not even be compatible in >the sense that one can replace the other in some section of code. Suppose I give the specification that a program must halt with the variable X equal to 1. There are lots of different programs that meet this specification, and they are not interchangeable, unless that is all we care about. A summation routine only cares that its arguments have an addition operation, but the routine that calls it may also want the sum of two polynomials to be a polynomial. There is no contradiction. Ralph Johnson -- University of Illinois at Urbana-Champaign