A question posed by IanGraham? at an OtNinetyNine goldfish-bowl session. His report on the session can be read at http://www.trireme.com/whitepapers/design/components/objects_vs_components.html.
What about classes? AreClassesObjects? AreComponentsLibraries?
My answer is that components are objects if their implementation has been specified in an object-oriented language. That is, if TheSourceCodeIsTheDesign and you do it with C++ or Java, then the compiled binary (or binaries) can be thought (in a logical sense) as instantiating objects to represent its implementation. But since implementation is separate from interface, components aren't necessarily objects. I can build a component in C or assembler or C++ or Java and still interact with it in the same way. That's one of the differentiators between CBD and OOD.
I would say that components are objects. Period. Let's not confuse "what it is" with "how it's implemented:" A COM / CORBA / other object may be implemented in any language, so it may or may not be built with objects. But, to the client of such an object, its implementation is immaterial; it is an object.
The reverse of this concept has also been seen: When OO first became a fad, I saw an advertisement for a word processor, saying that it was OO. They meant that it was built with objects, not that it (or any of the features it exposed to users) could be used as objects.
Saying that something "is OO" is ambiguous -- you have to know the granularity of the objects to know the level of benefit to expect. Objects in a SmalltalkLanguage program, for instance, are very fine-grained: Even the integer constant 123 is an object you can send custom application-defined messages to. CORBA objects, exposed from a legacy system are often very course-grained: "Order entry system" could be exposed, for example, as a single monolithic object.
So what I'm saying is that there is made with objects and usable as an object, and both are OO, but in different ways. -- JeffGrigg
Jeff, I like to think about OOD and CBD having a lot of overlap, but don't think CBD is limited to OO. If I build a component in VB, it's not OO. It's object-based, since VB isn't a fully object-oriented language. In addition, I think of languages and what you use to specify a component as being all about design and logical stuff. But when you run a programs that generates the system of components assembled into a solution -- compilers and linkers in many cases -- you are talking about physical runtime stuff. No longer does C++ or Java or VB matter to the idea of a component.
Perhaps this will strike a nerve with you, but think about what's really there at runtime. For instance, since you were involved in the COM discussion, I'm assuming you know COM, so think about this: regardless of the language I use to specify a component, can you describe the physical runtime nature of a component I instantiate with CoCreateInstance()? I'm looking for a response about structure -- please tell me your thoughts on what the memory looks like after CoCreateInstance() and what happens when I call a method on an interface pointer after doing a QueryInterface().
What do COM components look like? Are they objects?
The COM spec standardizes the interfaces between objects to something that is remarkably (even suspiciously ;-) like C++ objects. What you get from CoCreateInstance() is a pointer, which points to another pointer, which points to the first entry in an array of pointers to code. (These are "function pointers;" they point to the entry points of functions/methods.) To call a function, you use the C/C++ conventions for pushing arguments on the stack, including pushing your "CoCreateInstance" pointer on the stack as an additional argument "before" all the others. A rough C implementation would look something like this:
// (The following is a structure with three pointers in it.) struct ArrayOfFunctionPointers { HRESULT (*QueryInterface)(REFIID iid, void **ppvObject); ULONG (*AddRef)(void); ULONG (*Release)(void); }; struct TypicalCOMInstance { struct ArrayOfFunctionPointers *vtbl; // (other strictly private "instance variable" stuff here) }; struct TypicalCOMInstance *pComObject = getAnInterfacePointer(); (*(pComObject->vtbl->AddRef))();A "pointer to a COM interface" is essentially a pointer to an instance of an object. If you QueryInterface for IUnknown, the COM spec guarantees that the resulting pointer uniquely identifies the particular instance of the object.
An OO object is a linking of state and behavior. COM provides a convenient place to store state; you can put it before or after the location the interface pointer is pointing. And COM standardizes methods of invoking behavior, requiring that the pointer to the interface / instance be provided to each. So COM objects are real OO objects.
ComponentBasedDevelopment (CBD) is not "limited to OO," in that (1) you don't have to use an OO language to implement a component, and (2) you can use components from non-OO languages. But components are objects. Your non-OO client code can use the objects. The "insides" of a component object may or may not involve the use of finer-grained objects; the client doesn't know or care. But the component itself is an object.
I don't recommend looking at runtime structure to determine if something is OO or not. I can write a C++ program with well-factored classes and methods, and it may just happen that none of the methods are "virtual." (That is, instances have state and behavior, state is encapsulated, and so on, but there is no runtime polymorphic behavior: One can determine at compile time what version of each function will be called.) I say that this program is still ObjectOriented. Yet, at run time, the code is indistinguishable from ordinary modular procedural C code. The C++ program is OO, while the C program that would produce the same executable binary is not. (This can be done in Java too, with appropriate use of "final.") -- JeffGrigg
OK, you've convinced me. My argument is largely a rhetorical one, so I am willing to put it to rest. To tell you the truth, I think of a component that's been instantiated -- whatever the implementation or user's language was to specify the component or usage of it -- as an object that has been instantiated and that its interfaces are polymorphic, abstract ways of interacting with it.
I am willing to say a component is an object (or objects), but that language independence is one of the differentiators that you get under CBD that you don't get under OOD.
Surely components are classes, not objects (instances)?
I accepted Jeff's position out of being more abstract about what one considers an object. If we're going to be concrete again about exactly what these things are, then let's take a look at the question again.
I think you can say that a component is an object, and certain people may agree with you, but in the end you can't unequivocably equate them. It's like saying a family is an animal. If you are talking in the context of implementation, then a component can be defined as a class and have its state and behavior represented at runtime via objects instantiated by a factory. Whether this is true or not, the client is abstracted from it in CBD.
The client is oblivious to whether the component was coded in COBOL, C, VB, Java, Haskell, Delphi, Ada, or C++. But the client does things like:
So you can say a component is an object, or even try being more specific and say that a component is a specialization of an object, but there's more going on here that you're not considering that provides additional benefits and liabilities to the programmer in a component-based context. Language independence, binary interoperability, separation of interface from implementation, and a stronger emphasis on black box reuse are all things that are definitley true for components whether OO or not, but may or may not be true for OO development. In CBD, these things are usually enforced by an infrastructure like COM. In OO, enforcement is at the language level.
Your example loads a component class, instantiate a component, makes use of the component through polymorphic interfaces and then releases the component. How is this different from an object? It isn't. In fact, COM is an object model and nothing more.
The component models are built on top of COM. Components based on the COM infrastructure are COM objects that have ComponentFramework-specific COM interfaces. Similarly, compponents implemented using CORBA would support domain-specific CORBA interfaces.
I think there is some confusion between a component class and a component instance. What do people mean when they say "component"? Most of the literature uses "component" to refer to a component instance and "packaging" when they refer to a component class within some unit of deployment (a DLL or executable in the case of COM, a JAR file in the case of JavaBeans).
My example doesn't load a class, or at least in the way I think about it. My example initializes the COM infrastructure, then calls an API that results in the component either being loaded into the current process or being loaded into a foreign process.
As far as calling a component a class, I think about the class being the unit of instantiation for a component, and that interfaces are used to interact with it. But I think loading the component, instantiating it, and using it are done in three different ways. In COM, the unit of instantiation is described in IDL as a coclass. In CORBA IDL, it's called a class.
I think of COM as a standard component object model, but I try to separate it from being a component model or an object model. Component model implies a defined set of interaction protocols that make up a ComponentFramework. Object model implies what OO purists think we should create during problem definition phase before coding.
More specifically, COM is an infrastructure (I've called it a software or technology framework in other discussions) that abstracts me from the gory details of where a component is, how I instantiate it, how I query it for interfaces, the details of how I call methods against it, concurrency models, and whether it should be shutdown or not when I'm done using it. If it didn't exist, I'd have to do it myself if I wanted to reap the same benefits that it provides to me in the context of component-based development.
As I say below in response to Nat, my way of thinking about a component based on an abstraction like COM is that it can be conceptualized as an object, but it goes further than that -- it brings lots more into the equation. We can go from a single bit, to a byte, to a type, to a object (OK: class if it makes you feel better), to a component, and finally to a component framework. All of them raise the level of granularity, and something like COM helps you make the jump from objects/classes to components and component frameworks.
Finally, when I think about C++ development (C++ being a language that lets me specify classes that can be instantiated into objects) and component-based development (a higher-level concept that can be specified in any language but conforms to a language-independent specification for classes and objects and additionally enforces separation of interface from implementation yadda yadda yadda) I see that they are not equal.
(Or we wouldn't be having this discussion ;-) ;-)
You say "In CBD, these things are usually enforced by an infrastructure like COM. In OO, enforcement is at the language level.".
This is only true for O-O languages. OO is a way of designing a system that can be, and obviously has been, represented explicitly at the language level. However it doesn't have to be. I have written OO programs in C and Modula-2, neither of which are OO languages. CORBA is an OO system that is language independent and maps the CORBA concept of objects into the appropriate language concepts for each language it uses.
The only difference between CDB and OO, is that CDB focuses on a compositional approach to system construction by ThirdPartyBinding, and on a set of AbstractInteractions defined by a ComponentFramework. OO frameworks typically focus on customization of existing behaviour at particular "flex-points" within the framework (template methods, abstract factories and objects parameterised by strategies, for example). --NatPryce
When I said with OO development that enforcement is at the language level, I was implying (even if you are using an infrastructure like COM to do CBD or CORBA or RPCs or sockets or named pipes to do language-independent client-server interactions) the actual programming, subclassing, compile-time type checking, symbol resolution, and other things that go on (BTW, I think natively in C++) are being enforced at the language level. With CBD a la COM, it abstracts you from a lot of stuff. Here is what I mean:
I think about objects being at a finer level of granularity than components. I agree with your thoughts on CBD being more about composition. In fact, we treat components like objects when we use them. Where I listed the different things a client does when using components, I was trying to demonstrate that the relationship it has to the state and behavior of a component makes for a heavy case for calling a component something you instantiate and use just like an object. But I think components go further than that. Composition, black box reuse, and language independence are all things that are enforced at that level. Hence, components operate at a higher level of granularity than objects, and frameworks go even higher.
This discussion reminds me a lot of the ones we had for questions like what is a component, what is a framework, and what is a component framework.
I don't understand see this distinction of "level" you are talking about.
I think we agree that component-based development is just a particular discipline for constructing systems from objects, focusing on third-party composition and well-defined interaction protocols.
We also agree that a component component can be implemented in terms of objects.
Thus, there is no reason that the objects used to implement a component cannot themselves be composed according to the disciplines of CDB.
E.g: A particular component framework might only be applicable at some level of abstraction, but the components within that framework might be implemented using some other component framework.
OTOH, a component may be implemented in a non-OO language (Haskell for example) and that implementation hidden behind the polymorphic interfaces of the component. This is the same as in OO languages: a Java object can be implemented in Haskell and Java/JNI/C code written to link the polymorphic Java interface and the functional Haskell code.
Hmm... is this the same as LayeredFrameworks? It seems to be more like nested frameworks to me. Perhaps this is a new ProtoPattern? --NatPryce
Uh...I'm not sure about whether this is the same discussion. I think it's related, but not the same. To me, as the present theme in the Layered Component Framework pattern became more concrete, it began to be more about raising the level of abstraction. Higher and lower levels of granularity are similar, and can be the same, but are talking about two different ways to look at the same things. (jeez, I hope this doesn't spark another tangent ;-)
Many times on projects I've seen programmers go through a phase where they're trying to get a feel for where the line should be drawn on how many components should be classes or vice versa. You don't want every class in the system to be a standalone component because the overhead would be too high, especially in performance-sensitive areas. OTOH, you don't want all classes in the system to be wrapped by one component, because you'd be taking a step back to the monolithic programming days. So there must be an objective way of arriving at where you draw the line.
When I see these programmers "get it" and draw that line, usually each cleverly-designed component wraps several objects. Many times all components reuse the same objects from a lower-level framework (e.g., ATL, RogueWave Tools.h++, MFC, bespoke class frameworks, etc). When I look at one of the components in a project I'm currently working with, it consists of at least two dozen classes and is a whole design on its own. Components together have interaction protocols with their users that are higher level than the finer-grained interactions that go on amongst the classes they contain.
I've never ever seen it the other way around. So this tells me that classes/objects exist at a lower level or "layer" of granularity than components do. And as you said, it's not as simple as that -- a component can be an abstraction or extension of an existing ComponentFramework, or vice versa. But the concept of a framework is that it: consists of many components that've defined interaction protocols; that probably contain ComponentGlue, or provide users the with ability to use ComponentGlue to further compose the system; that in some way germane to the situation peform ThirdPartyBinding or provide a ComponentBus; etc., etc.
Hopefully, ComponentDesignPatterns does/will contain lots of the good ideas we see present in component-based systems that make them more "livable" as a whole.
I get where you're coming from!
The problem is the packaging overhead of the component system, rather than the direct granularity of the task performed by each component. The packaging overhead increases linearly with the number of components (I guess), so too many fine-grained components increases the packaging overhead to an unacceptable level.
These forces should be made more explicit in some of the packaging patterns (IndividualPackaging, GroupPackaging). However these forces are significantly different to require a new pattern or patterns. They are also related to the distribution patterns (ProcessBoundary, DistributionOfComponents and GoingThroughCustoms), since many of the trade-offs are the same when one decides the granularity of distribution. --NatPryce
Roger Sessions of ObjectWatch? has some interesting things to say on this topic: http://www.objectwatch.com/issue_28.htm .
He stresses the difference between interface design for objects and components. So your objects for an alarm application are finely grained and have few parameters per method, like so:
class AlarmService { public: AlarmList getAlarms() const; AlarmID reportAlarm(Alarm& alarm); }; class Alarm { public: AlarmID getID() const; string getName() const; string getSource() const; Severity getSeverity() const; void acknowledge(const string& user); void setSeverity(Severity severity); void setName(const string& name); . . . };but your AlarmService component would look like this:
interface AlarmService { AlarmID reportAlarm(in string name, in Severity severity); void acknowledgeAlarm(in AlarmID alarm, in string user); };This means that your client code works with a modular rather than an object-oriented interface. Unless you wrap your component interface up in more finely grained objects.
Also see ComponentDefinition, ComponentFramework, and ComponentFrameworkDefinition for some thoughts on the same subject...
See also: