What’s five days and has Jamie Oliver, moot, Bill Gates, Ken Robinson and Ze Frank in it?
Speaking of Design Patterns
Sean Murphy ably describes how an abstract class cluster works in Objective-C. NSString and every collection class is by contract a class cluster, and this is a useful pattern.
However, I take some exception with the example offered:
@interface SearchPluginParser
- (id)initWithPluginMIMEType:(NSString *)mimeType;
- (BOOL)parseSearchPluginAtURL:(NSURL *)searchPluginURL;
- (NSURL *)searchPluginURL;
- (NSString *)searchPluginName;
@end
Let’s see what can happen: you can init a parser. You can then either go parse a search plugin or look at the parsed search plugin’s URL and name… what? There’s implicit state built into the design. It’s implicitly part of the contract that you have to message parseSearchPluginAtURL: to go ahead with the rest of the class, and that searchPluginURL and searchPluginName only make sense after a search plugin has successfully been parsed. Even with that information, say you parse a search plugin and then parse an invalid search plugin. What’s searchPluginURL return now? nil? The URL of the last successful search plugin parsed? It’s horribly non-Cocoa-esque, but you also can’t exclude the possibility that it will throw an exception. (The Cocoa-tasting alternative is to highlight that the method may sometimes be expected to fail in a certain way due to network circumstances or uncontrolled data, and offer an NSError pointer out. Exceptions are more of an “oh shit!” expression which can be handled just for the sake of not bringing the application down.)
I’ve recently taken to an axiom: because there are more of them from many sources, it’s getting harder and harder to tell what you can do with objects, so a well designed class is one where the valid operations of the objects is always exactly what is available. I’m the first to admit that this kind of thinking can lead to brutal over-engineering, if applied ruthlessly or without thinking. In this way, it differs not from any other tool available for designing classes or the flow of objects.
The fun continues:
@interface SearchPluginParser (AbstractMethods)
// Abstract methods which should be implemented by subclasses:
- (BOOL)parsePluginData:(NSData *)searchPluginData;
@end
@interface SearchPluginParser (SubclassUseOnly)
// Private, concrete methods which should only be used by subclasses:
- (void)setSearchEngineName:(NSString *)newSearchEngineName;
- (void)setSearchEngineURL:(NSString *)newSearchEngineURL;
@end
The idea of class clusters seems somewhat missed. The idea, which Sean brings up, is to abstract away the concretions, and only depend on the abstractions. Let’s take a look at NSString’s header file:
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding>
/* NSString primitive (funnel) methods. A minimal subclass of NSString
just needs to implement these, although we also recommend
getCharacters:range:. See below for the other methods.
*/
- (NSUInteger)length;
- (unichar)characterAtIndex:(NSUInteger)index;
@end
NSString in itself is never instantiated, and provides base implementations of most of the surrounding methods. But the core is abstract. NSString handles the higher order operations, presuming that there is always a way to accomplish the lower level goals. The SubclassUseOnly category in the example, contrarily, prescribes a concrete storage for the data. (The data that, as we recall, is variously valid or not valid to request, depending on if you’ve called the parse method before.) It’s not particularly cumbersome to provide for your own storage, and it’s one of the things that should be concretely decided. But worse than that, it adds methods in the interface.
With that in mind, let’s remake the example.
@interface SearchPlugin
+ (id)searchPluginWithMIMEType:(NSString *)mimeType
atURL:(NSURL *)searchPluginURL;
- (NSURL *)url;
- (NSString *)name;
@end
@interface SearchPlugin (AbstractMethods)
- (id)initWithMIMEType:(NSString *)mimeType
fromURL:(NSURL *)searchPluginURL data:(NSData *)data;
@end
Some comments about this proposal:
The parser part of the equation becomes part of the implementation details handled by the concrete subclasses. Instead, the class is a search plugin. Given that parsing for a particular kind of search plugin (OpenSearch, Sherlock/Mycroft) is handled by a subclass, it doesn’t seem like much of a stretch.
The abstract superclass also handles going to the network. A well implemented example of this class cluster would check if the mime type is supported by some concrete subclass, then fetch the data over the network, then initialize an object of the right subclass, handing it the origin URL and the actual data as well as the MIME type. The origin URL may actually be required to resolve relative paths in the search plugin specification (although I’d call that bad form for an authoritative document), so it seems strange not to pass it to the method that does the parsing.
The design is more geared towards the client. I’m guessing that the client is not fascinated by whether the network failed, whether the URL was blacklisted in the phishing list or whether the format was invalid; it wants to get on with the rest of the search plugins it wants to deal with. (The client might be interested in these details, but such errors can be very implementation specific. Nevertheless, an
NSErrorparameter out can be added with plenty of precedence from Cocoa itself.)If constructing such a search plugin is possible, there’s no ifs and buts anymore: if it’s not
nil, it’s valid, and the accessors (which could probably be made into readonly properties) work just fine. The state situation from before has evaporated. Additionally, they can be put into a collection, something which is impossible for the parser; I’d imagine many clients of this class cluster would want to scan a web page for search plugins and return a bunch of them.This is still evidently a “godfather” variety class cluster, in which the class cluster knows every eventuality from the start. You could make it an inefficient (O(n)) open class cluster by simply iterating through every subclass and try to initialize objects of their class (if they’re handed data they can’t deal with, they return
nil). However, with the addition of one abstract class method (+ canProvideSearchPluginWith...), you could instead make it an open class cluster, where you could implement a loose check (for MIME type) separately.
The reason I don’t write more about class design is because it’s easy to get lost in it. I’m a bit sickened by the love for design patterns, although I do understand why they’re there. The last point I made, a subclass hierarchy that could be meta-interrogated for support, is a big deal in languages with a lesser object model, like C# and Java. If you want to emulate the elegance of it, you have to build some ugly scaffolding, but a by-product of that is people who want to talk for hours about the Factory pattern, which is what it’s called when you don’t have class methods.
Me, I just want my objects to contain what they contain, do what they do, and get on with hooking them together. I can’t wait until that becomes a “best practice”.
Mark
Jeff Atwood summarizes Markdown and its problems perfectly:
- Markdown is as close to a perfect plain-text language for formatting that I’ve seen.
- It’s got a few minor, but gnawing, flaws in its specification.
- It’s got a few more flaws in its implementation (which is both clever and robust, but more the former than the latter).
- It’s got many alternative implementations — too many, and too fragmenting.
- It needs a more solid reference implementation, more involvement and more direction; most could be gained by rallying more help and by moving towards the bazaar end of the spectrum, but also needs a different tinge of strong leadership. (Markdown is incredibly “opinionated“, but not so much “release early-release often”.)
What Jeff does not mention is that it’s hard to write a parser for it. I know because I’ve been trying to write a small Perl 6 parser for it in the past few days. (One of the big improvements in Perl 6 is that regexes are basically really good tools for parsing — you write more or less BNF with heavily modified regex syntax and can cart along side-effects, and it’s good enough to parse Perl 6 itself. You might say that in Perl 6, jwz’s Razor is proven untrue.)
I think Markdown deserves as much as for someone that knows the language (including corner cases like a code block in three layers nested blockquotes) and knows parsers to sit down and design a good production for Markdown. The only thing better than a prescriptive heap of source code is a prescriptive algorithm, which is what it comes down to.
Also, John Gruber needs to be more forthcoming about whether the fixed flaws (which validity he has acknowledged) should be corrected or made optional.
~
Appendix: The current state of my Perl 6 Markdown parser. Public domain, for all I care; I’m sure there are better ways to arrange the parsing, but this is where I’ve gotten. I know that this doesn’t even get to most of the interesting block stuff, like blockquotes and nesting them, but that kind of recurring prefix carrying state is also something that’s hard to specify in a parser, which is why it’s not here yet.
use v6;
grammar Markdown {
token TOP {
<blox>*
}
regex blox {
[<block_construct>|[<inx>+?]] [\n|$]
}
regex block_construct {
<blank_line> | <link_label_spec> | <header_spec>
}
regex header_spec {
<setext_header_spec> | <atx_header_spec>
}
regex setext_header_spec {
^^ <header_text> \n <[=\-]>+ $$
}
regex atx_header_spec {
# Strip leading space from header? spec not clear
# ^^ \# ** 1..6 <[\ \t]>+ <header_text> \#* $$
^^ \# ** 1..6 <header_text> \#* $$
}
regex header_text {
[.*?]
}
regex blank_line {
^^\s*$$
}
regex link_label_spec {
^^\s ** 0..3 <link_label> ':'
<[\ \t]>+ <link_label_spec_url> \s*
<link_label_spec_title>? $$
}
regex link_label_spec_url {
('<' $<url>=[\N*?] '>') |
($<url>=[\N*?])
}
regex link_label_spec_title {
['\'' $<title>=[.*?] '\''] |
['"' $<title>=[.*?] '"'] |
['(' $<title>=[.*?] ')']
}
token link_label {
'[' $<label>=[.+?] ']'
}
regex inx {
<inline_construct> | <atom>
}
token inline_construct {
<link> | <inline_pres>
}
token inline_pres {
<em>
}
token inx_link_title {
<inline_pres>
}
regex link {
'[' $<inner>=[<inx>+?] ']' ' '? [<link_direct>|<link_deferred>]
}
token link_direct {
'(' .+? [$<title>=[' "' <inx_link_title> '"']]? ')'
}
token link_deferred {
<link_label>
}
token em {
(<[*_]> ** 1..2) $<inner>=[<inx>+?] $0
}
token atom {
['\\' $<ch>=<[*#.\\\-]> | $<ch>=.]
}
}
my $testcase = "x*y[z#\\#] [a]å*
__ab__c
[a]: /foo (title)
[b]: /foo
[c]: /bar \"title\"
[d]: /bar 'title'
d*ef*";
$testcase = "abc
===
### bfx";
my $match = Markdown.parse($testcase);
say $match.perl; # print the AST
iPhone Cards
At the top of my list for iPhone 4.0 is multitasking with, basically, the Pré model. Go on, Apple, just take it. If you still cling to the concept that ideas are patentable, you can duke it out in court while the customers benefit.
This is exactly how it’s done. (Well, almost exactly; Pré allows for an application to open many cards, and this is just per application. But the best you can do with what’s there today.) Almost enough to make me want to go back to jailbreaking again. Actually, now might be the best time, since it’s going to be a quiet period before the next major update for a while.