Hi,
I noticed that methods are grouped in categories and protocols. What's the difference between categories and protocols? O:-) |
Fernando wrote:
> I noticed that methods are grouped in categories and protocols. What's > the difference between categories and protocols? O:-) (I don't know if you want a serious answer or not, here is one anyway...) Protocols are about defining the roles and contracts between objects. They specify groups of messages that an object is required to understand in order to be used in some context. They are intended to be used to express (more or less informally) the 'type' of an object as used in that context. (They can be likenened to Java's "interfaces" which are used in a similar way, albeit more formally) You'll see lots of method comments stating that some argument should be, say, a <SequenceableCollection>, which means that any object that conforms to the <SequenceableCollection> protocol is acceptable as a value for that argument. Protocols are about understanding the structure of /programs/. Categories are only about browsing. They can be used to organise the methods into /overlapping/ groups of related methods. When used properly, they help the reader understand the structure of a /class/. Both concepts are occasionally used (slightly hackishly, IMO) to mean something else. E.g. the Model of an MVP triad is included in command routing if its class is declared to conform to the protocol <commandTarget> (IIRC). And the method category '**auto generated**' is used to mark methods that the system can and should overwrite with new versions when it's generating methods. Neither of these applications is typical of the use of protocols or categories. -- chris |
On Thu, 25 Nov 2004 12:54:42 -0000, "Chris Uppal"
<[hidden email]> wrote: >Fernando wrote: > >> I noticed that methods are grouped in categories and protocols. What's >> the difference between categories and protocols? O:-) > >(I don't know if you want a serious answer or not, here is one anyway...) Hhmmm... yes, why not? O:-) >Protocols are about defining the roles and contracts between objects. They >specify groups of messages that an object is required to understand in order to >be used in some context. They are intended to be used to express (more or less >informally) the 'type' of an object as used in that context. (They can be >likenened to Java's "interfaces" which are used in a similar way, albeit more >formally) You'll see lots of method comments stating that some argument >should be, say, a <SequenceableCollection>, which means that any object that >conforms to the <SequenceableCollection> protocol is acceptable as a value for >that argument. Protocols are about understanding the structure of /programs/. >Categories are only about browsing. They can be used to organise the methods >into /overlapping/ groups of related methods. When used properly, they help >the reader understand the structure of a /class/. Maybe I still don't get it right, but it seems like overlapping/redundant concepts to me.... Anyway, what's the usual practice when creating new methods? Should you always include them in a category or protocol? O:-) |
Fernando wrote:
> Maybe I still don't get it right, but it seems like > overlapping/redundant concepts to me.... I don't think there's /that/ much overlap, not in the concepts anyway (the implementations are much more alike). One is about describing the contract between interacting objects, the other is a way of making the CHB more useful... > Anyway, what's the usual practice when creating new methods? Should > you always include them in a category or protocol? O:-) I can only tell you what /I/ do. I use explicit protocols only rarely (less than once per "project"), which is mainly because I don't often need to specify the contracts between objects explicitly and separately from some shared abstract superclass. When I /do/ use them, I don't add methods to protocols at all, not in the sense (I think) you mean. There are two steps to using a protocol -- (1) define the protocol and the selectors it requires, (2) define which classes conform to that protocol. Once defined, the protocol is intended to be fairly static -- there's no point at all to a protocol that changes as often as the classes that use it. Here's a real example. I wanted to put together a small library for plotting graphs. One of the (rather tricky) decisions was how to provide data to the library for plotting -- what protocol the plottable data-objects should obey. After a great deal (far too much) messing around trying various things, settling on ideas, and then changing my mind again, I've eventually settled on a design where the data that is plotted must be supplied as an object conforming to the protocol <PlottableData>. <PlottableData> is defined to have two selectors #size and #do: (where both are expected to work as in Collections, and where #do: should iterate over objects that themselves respond to #x and #y with Numbers). (As an aside, the conditions in brackets in the previous sentence cannot be expressed (or cannot be recorded) in Dolphin protocols, and that, together with the fact that they aren't saved in packages as first-class objects, is why I don't make more use of them than I do.) So now I have a (semi-) formal expression of what it takes for data to be plottable by my plotting package -- that's the whole point of using protocols. From now on there'd be no point in adding methods to the protocol (unless I change my mind about the fundamental design -- again...). However I can and do create classes who instances conform to the protocol, so I can and do create classes with methods #size and #do:. When I create such classes, I declare them to conform to <PlottableData>, but not by adding methods to a protocol, but by adding the /class/ to the protocol[*] (e.g. using the Protocol Browser). There is no /functional/ benefit in making this explicit declaration, but it does help to /document/ the relationship between the objects that are <PlottableData> and the objects that will render them on-screen or to a printer. Once I have all that set up, if I define a new class and declare it to conform to <PlottableData> then Dolphin will check to see if that class defines #size and #do:, and will warn me if it doesn't (in fact it gives me the option to create dummy methods with those names). Similarly, if I did change my mind and add a new method to the protocol, #boundingRectangle say, then Dolphin would warn me about any classes that conformed to <PlottableData> but which didn't already have a #boundingRectangle method. Method categories, on the other hand, are completely different (and much simpler). All they do is give you a better way of navigating around the definition of a class. E.g. when I create a new Presenter class, I'll nearly always have method in categories 'commands', #event handling', 'initialising' and 'subpresenters' (and probably lots more too). For instance I like to define an "accessor" method for each named subpresenter, so they all go in 'subpresenters', there will normally be an #initialize method in 'initialising'. #createComponents is about initialising sub-presenter, so that goes into both 'subpresenters' and 'initialising'. Similarly #createSchematicWiring goes into both 'initialising' and 'event handling'. And so on. The categories let me find the methods I'm interested in quickly, and also allow me to restrict the methods list to just the ones I'm interested in at any one time. I find them very useful, and therefore make an effort always to put methods into sensible categories when I first create them. HTH... -- chris |
A small augmentation to Chris' excellent overview of the protocols vs
categories: "Chris Uppal" <[hidden email]> wrote in message news:[hidden email]... >... > From now on there'd be no point in adding methods to the protocol (unless > I > change my mind about the fundamental design -- again...). >... I wouldn't state it that strongly. One of the reasons to use protocols is to take advantage of the way the system enforces the interface contract when you do add to the protocol. This can be handy when several classes implement a protocol in order to serve in a particular role. The protocols help to add some of the support you might get from a statically typed language in that situation. >... > There is no /functional/ benefit in making this explicit declaration, but > it > does help to /document/ the relationship between the objects ... I agree that the main benefit is in declaring the relationship explicitly, but it is also possible to use protocols to perform dynamic enquiry as to whether an object can be used in a particular role. Although protocol conformance tests are a form of switch on type, and should thus be used sparingly, they are cleaner than #isKindOf: tests because they don't depend on the implementation inheritance hierarchy. In this sense protocols can be used as a replacement for defining loads of #isXXXX test methods (i.e. is-a tests) that end up polluting Object. I agree with your criticisms of protocols BTW (and would add the additional one that they have no inheritance capability). Ironically protocols did originate in an idea to extend the category mechanism. In exploring this idea it quickly developed into the protocol mechanism, which as you have explained has a completely different purpose. However having seeded protocols from the category mechanism they have almost inexorably ended up with the limitations you describe. Protocols should probably be represented as classes (or at least a special type of behaviour), but I haven't explored that idea in any detail. Lastly there is a paper on "SmallInterfaces" available somewhere on the net that describes Dolphin's protocols as related work. This paper's description of Dolphin's protocols contains a number of factual inaccuracies, not the least of which is that it states that the protocols have no persistent form. Protocols are saved out with class file outs. This is not ideal, because as you say protocols should be represented as code artifacts in their own right in the package system. However it does preserve the protocol definitions. Regards Blair |
In reply to this post by Chris Uppal-3
Chris Uppal wrote:
>Here's a real example. I wanted to put together a small library for plotting >graphs. One of the (rather tricky) decisions was how to provide data to the >library for plotting -- what protocol the plottable data-objects should obey. >After a great deal (far too much) messing around trying various things, > > Chris, coming from the Java world - would it be fair of me to think of protocols along the lines of java's "interfaces", where you declare an interface, which a class can "implement": public interface Foo { void someMethodYouMustImplement(); } public class Bar implements Foo { public void someMethodYouMustImplement() { ..... } } ? |
In reply to this post by Blair McGlashan-3
Blair McGlashan wrote:
> > From now on there'd be no point in adding methods to the protocol > > (unless I > > change my mind about the fundamental design -- again...). > > ... > > I wouldn't state it that strongly. I was about to agree, but on reflection, it seems to me that changing a protocol /is/ changing the fundamental design. (At least if you exclude the case where you are tinkering with a protocol trying to improve the way it captures some pre-existing design -- compare the case of changing the design documentation which can either be because the design has changed, or because the documentantation wasn't good/complete/clear enough). But still, the way I expressed it over-emphasised the static-ness of protocols -- after all, it's not as if design changes are all that rare (and that's one of the reasons for using Smalltalk in the first place...) > > There is no /functional/ benefit in making this explicit declaration, > > but it > > does help to /document/ the relationship between the objects ... > > I agree that the main benefit is in declaring the relationship explicitly, > but it is also possible to use protocols to perform dynamic enquiry as to > whether an object can be used in a particular role. Agreed; I think I'd forgotten about that scenario when I was writing the post :-( In practise, though, I tend not to use protocols for that purpose (I don't think I've ever done so), even though I agree that it's cleaner and more general than an #isKindOf: check. One reason is practical -- I find that protocols in their current implementation are too lightweight for me to feel comfortable using them to affect runtime behaviour. They have a tendency to disappear if you aren't careful (I've "lost" protocols before), and adding pre-existing classes to protocols is a little awkward (eg. in my earlier example, a SequencableCollection of Points is in fact suitable for use as a <PlottableData>, but adding the corresponding explicit declaration would require some messing around). More theoretically, I'm not comfortable with the fact that classes must /explicitly/ declare themselves to conform to some protocol. It seems too declarative to me, and I've become very hostile to the declarative view of programming in recent years (especially in Smalltalk). I think I'd be happier testing whether the object "implements" the protocol in the sense of a #respondsTo: for each of the selectors in the protocol. I'm not absolutely certain that this unease is actually defensible (after all, an #isKindOf: check is even /more/ declarative), but that's how I've been seeing it to date. > Protocols should > probably be represented as classes (or at least a special type of > behaviour), but I haven't explored that idea in any detail. Thinking about this a little, I don't agree. I know that SmallInterfaces takes that approach, but the more I think about it, the less sense it makes (to me, anyway). Protocols (obviously) aren't some kind of class -- there is no "is-a" relationship that we are trying to model by making Protocol a subclass of Behavior; indeed (if anything) it seems that Behaviour should be a subclass of Protocol. I'd say that a protocol is able to answer the question, "is <some object> suitable for use in <some context>" and that really has very little to do with classes. In practise, for many contexts, knowing whether an object is an instance of some class is actually a very good heuristic for answering that question, so there's a case to be made that /classes/ should be able to act as /protocols/ -- not the other way around (you could think of it that every class implicitly defines a protocol). Another practical point is that it would be nice to be able to associate text with each selector in a protocol. That would either be a comment would be copied into the body of corresponding auto-generated placeholder methods, or could even be the entire source for the placeholder methods (one could go further and specify method categories for each selector too). I can see that that makes a protocol look rather like a class, but I don't think the similarities are anything other than misleading. > Protocols are saved out with class file outs. > This is not ideal, because as you say protocols should be represented as > code artifacts in their own right in the package system. That would be fairly tricky to get right -- the interaction with the pre-requisites system would be quite complicated, at least if you wanted to allow protocols to be "retro-fitted" to existing classes without awkwardness. -- chris |
In reply to this post by talios@gmail.com
Mark Derricutt wrote:
> Chris, coming from the Java world - would it be fair of me to think of > protocols along the lines of java's "interfaces", where you declare an > interface, which a class can "implement": I'd say they are almost identical ideas; but I'd also add a few caveats: The first caveat is that there's a fairly common tendency in the Java programming world to think of interfaces as sort of "cut down" abstract classes. (This manifests, for instance, in claims that Java does have multiple-inheritance, but that it is restricted to interfaces. Another manifestation, I think, is that many programmers would have difficulty expressing /why/ there are significant differences between a completely abstract class (no method bodies at all) and an interface with the same apparent "contents") IMO that tendency, although quite common, is flat-out wrong (it's encouraged by the unfortunate similarity -- handy but misleading -- between the syntax used for classes and interfaces). I see both Java interfaces and Dolphin protocols as being about how you know/document/test whether a given object is suitable for use in some context. Certainly Dolphin protocols are not in any sense "cut down classes". The second caveat is that, unlike Java interfaces, Dolphin protocols are not used for static type checking (obviously!). One effect of this is that there is less need for as many protocols -- you only define or use a protocol where you think it has positive value, rather than being forced to do so to keep the compiler happy. The last caveat is also related to type checking. In Java, interfaces can be used to loosen the coupling that the type-checker would otherwise cause between different classes. That use (which is perfectly legitimate, IMO) allows you to replace the declaration of some variable: SomeClass aVariable; with a reference to a suitable interface: SomeInteface aVariable; (where SomeClass implements SomeInteface). The point is not only that "SomeInteface" is more general than "SomeClass" (thus allowing greater flexibility), but also it avoid mentioning "SomeClass" /at all/, and so avoids coupling between the class and the code that uses its instances. Smalltalk, in contrast, doesn't introduce that kind of coupling in the first place, so Dolphin protocols have no such role. In fact you could think of Dolphin protocols as a way of /increasing/ the coupling; available for use in cases where you don't think Smalltalk's normal semantics express the connection strongly enough. -- chris |
In reply to this post by Chris Uppal-3
"Chris Uppal" <[hidden email]> wrote in message
news:[hidden email]... > Blair McGlashan wrote: > ... >> but it is also possible to use protocols to perform dynamic enquiry as to >> whether an object can be used in a particular role. > > Agreed; I think I'd forgotten about that scenario when I was writing the > post > :-( > > In practise, though, I tend not to use protocols for that purpose (I don't > think I've ever done so), even though I agree that it's cleaner and more > general than an #isKindOf: check. One reason is practical -- I find that > protocols in their current implementation are too lightweight for me to > feel > comfortable using them to affect runtime behaviour. They have a tendency > to > disappear if you aren't careful (I've "lost" protocols before), There are certainly a couple of circumstances where that might happen: 1) The protocol implementation in a class depends on loose methods in other packages than that which owns the class 2) You rename or delete a method that is implementing a protocol. (1) is really a failing of the way protocols are represented in package system. As we have discussed protocols are always associated with a class (and included in the class file out), and this means that the protocol is not preserved in any of the packages. The IDE trys to protect you against (2), for example by warning you if you attempt to delete a method contributing to the implementation of a protocol. Unfortunately it doesn't give the same kind of warnings when using some refactorings (especially method signature changes); these may silently drop the protocol from the affected class if it not longer conforms to the protocol. It can also happen when filing in or out chunks as the system will always maintain the correctness of the protocol conformity and that may mean dropping a protocol from classes that will not conform after a change. >...and adding > pre-existing classes to protocols is a little awkward (eg. in my earlier > example, a SequencableCollection of Points is in fact suitable for use as > a > <PlottableData>, but adding the corresponding explicit declaration would > require some messing around). Well there might be cases where you could make assertions about pre-existing classes implementing protocols that you yourself define, although I don't think the SequenceableCollection of Point is a good example since none of the Collection classes restrict the class of elements in any way. Regardless I think that once again this is an issue of the way the protocols are (not) represented in the package system. > > More theoretically, I'm not comfortable with the fact that classes must > /explicitly/ declare themselves to conform to some protocol. It seems too > declarative to me, and I've become very hostile to the declarative view of > programming in recent years (especially in Smalltalk). For me that is the whole point of protocols - they allow you to add additional meta-information about the roles supported by a class. >..I think I'd be happier > testing whether the object "implements" the protocol in the sense of a > #respondsTo: for each of the selectors in the protocol. I'm not > absolutely > certain that this unease is actually defensible (after all, an #isKindOf: > check > is even /more/ declarative), but that's how I've been seeing it to date. In that case you might prefer SmallInterfaces. Personally I think that approach is completely misguided - it might be pure coincidence that a class implements a particular set of selectors. The fact that a class responds to a particular selector says nothing about the semantics. You are just taking an educated guess that it probably implements the protocol. Of course good style in Smalltalk recommends the use of specific, intention revealing selectors (e.g. in PlottableData your might have #addCoordinate: rather than #add:), and this might make the guess more likely to be correct, but it doesn't guarantee it. >> Protocols should >> probably be represented as classes (or at least a special type of >> behaviour), but I haven't explored that idea in any detail. > > Thinking about this a little, I don't agree. I know that SmallInterfaces > takes > that approach, but the more I think about it, the less sense it makes (to > me, > anyway). Protocols (obviously) aren't some kind of class -- there is no > "is-a" > relationship that we are trying to model by making Protocol a subclass of > Behavior; indeed (if anything) it seems that Behaviour should be a > subclass of > Protocol. I'd say that a protocol is able to answer the question, "is > <some > object> suitable for use in <some context>" and that really has very > little to > do with classes. In practise, for many contexts, knowing whether an object > is > an instance of some class is actually a very good heuristic for answering > that > question, so there's a case to be made that /classes/ should be able to > act as > /protocols/ -- not the other way around (you could think of it that every > class > implicitly defines a protocol). > Whoever sits on top, there are powerful reasons to explore the idea, not the least of which is the potential to unify concepts. Apart from being consistent with Smalltalk's general philosophy, this avoids introducing another code grouping concept that must be understood by programmer and tools. It also opens up the possibility of other capabilities such as mix-ins, and multiple implementations of the same selector with visibility dependent upon the role in which one is using an object. > Another practical point is that it would be nice to be able to associate > text > with each selector in a protocol. That would either be a comment would be > copied into the body of corresponding auto-generated placeholder methods, > or > could even be the entire source for the placeholder methods (one could go > further and specify method categories for each selector too). I can see > that > that makes a protocol look rather like a class, but I don't think the > similarities are anything other than misleading. > > >> Protocols are saved out with class file outs. >> This is not ideal, because as you say protocols should be represented as >> code artifacts in their own right in the package system. > > That would be fairly tricky to get right -- the interaction with the > pre-requisites system would be quite complicated, at least if you wanted > to > allow protocols to be "retro-fitted" to existing classes without > awkwardness. It would certainly complicate the prerequisites tracing (e.g. where a protocol owned by a package depends on loose methods in other packages tthis would mean tracing the ownership of the methods that support a protocol, as well as the conforming class with which a protocol has been associated). Regards Blair |
Free forum by Nabble | Edit this page |