API Design Shawn M Moore Best Practical Solutions http://sartak.org Thursday, September 10, 2009 Presented YAPC::Asia, 2009-09-10. Tokyo Institute of Technology, Tokyo, Japan. GoogleのテックトークでもAPI設計の話が出てました Thursday, September 10, 2009 There is a good Google Tech Talk on "how to design an API and why it matters". There isn't a whole lot of overlap between this talk and that one. Watch that one. http://www.youtube.com/watch?v=aAb7hSCtvGw CC-BY-SA Yuval Kogman, 2006 Thursday, September 10, 2009 At YAPC::NA 2009, this guy, Hans Dieter Pearcey aka confound aka hdp, presented a talk about Dist::Zilla. http://www.flickr.com/photos/nufn/179250512/ CC-BY-SA Yuval Kogman, 2006 Thursday, September 10, 2009 Dist::Zilla was written by this other guy, Ricardo Signes, aka rjbs. http://www.flickr.com/photos/nufn/179250812/ CC-BY-SA Hans Dieter Pearcey, 2009 Thursday, September 10, 2009 Dieter presented this slide about Dist::Zilla's pluggable design. I loved it and I wanted to devote an entire talk to its glory. http://weftsoar.net/~hdp/dzil/ Moose Path::Dispatcher HTTP::Engine Dist::Zilla IM::Engine 今日はこれらのプロジェクトが持つクールな APIの話をします Thursday, September 10, 2009 I'm here to highlight really cool API designs that these projects have. In particular, they design for extensibility and pluggability. Extensibility is really important to the current and future success of these projects. CC-BY-SA-NC Will Spaetzel, 2005 Thursday, September 10, 2009 If you haven't noticed yet, this talk is going to be very Moose-heavy. All those modules have the Moose nature. http://www.flickr.com/photos/redune/6562798/ THE SECRET 秘訣、といってもみなさんご存じだと思いますが Thursday, September 10, 2009 There is a poorly kept secret for designing great APIs. I hope that all of you already do this, but you probably do not do it enough. THE SECRET WRITE 大事なのはテストを書く、ということTESTS Thursday, September 10, 2009 Write tests. WRITE TESTS WRITEWRITE TESTS TESTS WRITEWRITE TESTS TESTS 血管切れるまでテストを書いてください Thursday, September 10, 2009 Write so many tests your ears bleed. I am not joking! DO THIS WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITEWRITE WRITE WRITE WRITE WRITE WRITE なにはなくとも、とにかくテスト Thursday, September 10, 2009 If you remember nothing else, remember to write tests! Test! use base 'Class::Accessor::Fast'; __PACKAGE__->mk_ro_accessors('birthday'); use Moose; has birthday => (is => 'ro'); テストを書けば使いやすいAPIかどうかがわかります Thursday, September 10, 2009 Write tests so you can tell if your API is painful to use. Which of these would you rather be stuck with? Make it painless for your users. Some of them might be using your module a lot. If it's tedious to use your module... Test! 使いづらいモジュールは使ってもらえませんから Thursday, September 10, 2009 ... then you'll piss your users of. They'll leave and use some other module, or worse, find out where you live. Test! Thursday, September 10, 2009 This is Jesse Vincent, the nicest guy in the world :) Test! Thursday, September 10, 2009 Test! Thursday, September 10, 2009 Moose package Point; use Moose; has x => ( is => 'ro', isa => 'Num', ); この先はMooseベースの話なので少し解説 Thursday, September 10, 2009 Moose serves as the foundation for the rest of the talk, so I want to explain what it "got right" in terms of its API. These next few slides are difcult but it will get clearer and less heady, so wake up soon if you space out. Metaobject Protocol Class::MOP MooseはClass::MOPのラッパです Thursday, September 10, 2009 Moose is built on top of a metaobject protocol. This is Class::MOP. See my "Extending Moose for Applications" talk for a proper introduction to the metaobject protocol http://sartak.org/talks/yapc-na-2009/extending-moose/ Metaobject Protocol has cache => ( is => 'ro', ); 要するにクラスの各パーツはすべてオブジェクトです Thursday, September 10, 2009 The MOP is vital to Moose's operation. Basically, it means that every part of your class is represented by an object. Metaobject Protocol has cache => ( is => 'ro', ); Moose::Meta::Attribute hasはMoose::Meta::Attributeのインスタンスを作ります Thursday, September 10, 2009 When you say "has" it creates an instance of the Moose::Meta::Attribute class, which holds information like the attribute's name, its type constraint, default value, etc. Metaobject Protocol has cache => ( is => 'ro', ); Moose::Meta::Method::Accessor これでcacheというアクセサメソッドが作られます Thursday, September 10, 2009 The is => 'ro' option creates a "cache" method in your class. It also creates an object of class Moose::Meta::Method::Accessor to represent that "cache" method. Metaobject Protocol class PersistentAttr extends Moose::Meta::Attribute { … } has cache => ( metaclass => 'PersistentAttr', is => 'ro', ); Mooseのクラスを拡張すれば独自機能も追加できます Thursday, September 10, 2009 This is important because we can subclass Moose's class to add our own special logic, such as making the cache persist across processes. Subclassing and adding logic is ordinary object- oriented programming! Metaobject Protocol role PersistentAttr { … } has cache => ( traits => ['PersistentAttr'], is => 'ro', ); アトリビュートオブジェクトにロールを組み込むこと もできます Thursday, September 10, 2009 We can also specify roles to apply to cache's attribute object. This is slightly better because it means a single attribute can have many extensions. Just like how it's better to design with roles than subclasses in ordinary programming. MooseX ほとんどのMooseXはMOPを利用しています Thursday, September 10, 2009 The metaobject protocol powers most of the MooseX modules. In my opinion, the metaobject protocol is responsible for a very large part of Moose's popularity. The other reason for Moose's popularity is it enables concise class code. Sugar Layer Mooseはシュガー層がきれいに分離しているのも特徴 です Thursday, September 10, 2009 Moose also makes a very clean separation between its sugar layer and the rest of the system. Sugar Layer my $class = get_class(); あるクラスを利用したいとします Thursday, September 10, 2009 Say you wanted to get ahold of some class... Sugar Layer my $class = get_class(); $class->has( birthday => ( is => 'ro', ) ); hasはメソッドではないのでこれではだめです Thursday, September 10, 2009 Then add an attribute to it. This doesn't work because "has" is not a method. Its first parameter is supposed to be the attribute name, not the class you're adding the attribute to. Sugar Layer my $class = get_class(); no strict 'refs'; *{$class.'::has'}->( birthday => ( is => 'ro', ) ); だからといってhasを 関数として呼ぶのはばかげています Thursday, September 10, 2009 So we have to call $class's "has" as a function. This kind of thing is ridiculous. Maybe the other class has used "no Moose" so that "has" is deleted. Or perhaps it renamed "has". Sugar Layer my $class = get_class(); no strict 'refs'; *{$class.'::has'}->( birthday => ( is => 'ro', ) ); それに見づらいですよね Thursday, September 10, 2009 Not to mention how ugly this mess is. Sugar Layer Class::MOP::Class ->initialize($class) ->add_attribute( $name, %options); hasは基本的にはadd_attributeのラッパです Thursday, September 10, 2009 If we look at the source code of Moose, we can see "has" is basically a wrapper around the "add_attribute" method of the Class::MOP::Class instance. Sugar Layer my $class = get_class(); $class->meta->add_attribute( birthday => ( is => 'ro', ) ); これでずいぶんマシになりました Thursday, September 10, 2009 Much better. There's no messy syntax. This can be used outside of $class's namespace just fine. This also works if class has cleaned up after Moose with "no Moose" or namespace::clean. Sugar Layer use MooseX::Declare; class Point3D extends Point { has z => (…); after clear { $self->z(0); } } シュガー層が分離しているので修正も楽です Thursday, September 10, 2009 Having a clean sugar layer means that other people can write better sugar. I like the idea of providing a separate Devel::Declare-powered sugar layer in a separate distribution. It forces you to cleanly separate the pieces. Path::Dispatcher use Path::Dispatcher::Declarative -base; on ['wield', qr/^\w+$/] => sub { wield_weapon($2); } under display => sub { on inventory => sub { show_inventory }; on score => sub { show_score }; }; YourDispatcher->run('display score'); これはJifty::DispatcherをProphetで 使うために書きました Thursday, September 10, 2009 Path::Dispatcher is a standalone-URI dispatcher. I wrote it because I wanted Jifty::Dispatcher for Prophet's command-line interface. This is its sugar layer. Like Moose, it has a clean, extensible API if you want the freedom to do unusual things. Path::Dispatcher::Declarative use Sub::Exporter -setup => { exports => [ on => \&build_on, under => \&build_under, …, ], }; もともとはSub::Exporterを使っていました Thursday, September 10, 2009 It used to be that Path::Dispatcher::Declarative was implemented as an ordinary Sub::Exporter-using module. Path::Dispatcher::Declarative use Sub::Exporter -setup => { exports => [ on => \&build_on, under => \&build_under, …, ], }; でもこれではまったく拡張性がありません Thursday, September 10, 2009 This is not at all extensible. You can't change the meaning of "on" or "under" because these are hardcoded. Reusing this sugar would be painful as well. Path::Dispatcher::Builder Robert Krimen "grink" Robert Krimenが拡張したがったので Thursday, September 10, 2009 This was fine for a few weeks, but then Robert Krimen started using Path::Dispatcher. And he wanted to extend it for a module he was writing called Getopt::Chain. Path::Dispatcher::Builder return { on => sub { $builder->on(@_) }, under => sub { $builder->under(@_) }, …, }; サブクラス化してロジック変更できるようにしました Thursday, September 10, 2009 Path::Dispatcher::Builder makes the sugar layer creation use OOP. This let Robert subclass Path::Dispatcher::Builder and use it for his own modules. He can reuse the regular dispatcher logic, tweak it by overriding methods, and add his own behavior. grink++ OOのシュガーは本当にいいですよ Thursday, September 10, 2009 OO sugar is
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages83 Page
-
File Size-