Google API 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 tha



Similar documents
C. S2 X D. E.. (1) X S1 10 S2 X+S1 3 X+S S1S2 X+S1+S2 X S1 X+S S X+S2 X A. S1 2 a. b. c. d. e. 2

L1 What Can You Blood Type Tell Us? Part 1 Can you guess/ my blood type? Well,/ you re very serious person/ so/ I think/ your blood type is A. Wow!/ G

\615L\625\761\621\745\615\750\617\743\623\6075\614\616\615\606.PS

-2-

NO

open / window / I / shall / the? something / want / drink / I / to the way / you / tell / the library / would / to / me

AERA_English_CP_Sample_org.pdf

国際恋愛で避けるべき7つの失敗と解決策

第16回ニュージェネレーション_cs4.indd

平成29年度英語力調査結果(中学3年生)の概要

2



鹿大広報149号

P

高等学校 英語科



What s your name? Help me carry the baggage, please. politeness What s your name? Help me carry the baggage, please. iii

はじめに


sein_sandwich2_FM_bounus_NYUKO.indd

高2SL高1HL 文法後期後半_テキスト-0108.indd

Answers Practice 08 JFD1

NextWebBtoB_BtoC _suwa.pdf

P

西川町広報誌NETWORKにしかわ2011年1月号

日本語教育紀要 7/pdf用 表紙


untitled

Introduction Purpose This training course describes the configuration and session features of the High-performance Embedded Workshop (HEW), a key tool

S1Šû‘KŒâ‚è

-2-

elemmay09.pub


第17回勉強会「英語の教え方教室」報告

J.S

,,,,., C Java,,.,,.,., ,,.,, i

WASEDA RILAS JOURNAL 1Q84 book1 book One Piece

untitled

教育実践上の諸問題

Page 1 of 6 B (The World of Mathematics) November 20, 2006 Final Exam 2006 Division: ID#: Name: 1. p, q, r (Let p, q, r are propositions. ) (10pts) (a



Cain & Abel

キャリアワークショップ教師用

千葉県における温泉地の地域的展開

Microsoft Word - j201drills27.doc


20 want ~ がほしい wanted[-id]want to 21 her 彼女の, 彼女を she 22 his 彼の, 彼のもの he 23 how どのように, どうなのか, どれくらい how to <How!> How many? How much? How long? How ol


1 ( 8:12) Eccles. 1:8 2 2


Bull. of Nippon Sport Sci. Univ. 47 (1) Devising musical expression in teaching methods for elementary music An attempt at shared teaching

™…

3

目次 1. レッスンで使える表現 レッスンでお困りの際に使えるフレーズからレッスンの中でよく使われるフレーズまで 便利な表現をご紹介させていただきます ご活用方法として 講師に伝えたいことが伝わらない場合に下記の通りご利用ください 1 該当の表現を直接講師に伝える 2 該当の英語表現を Skype

自分の天職をつかめ


Read the following text messages. Study the names carefully. 次のメッセージを読みましょう 名前をしっかり覚えましょう Dear Jenny, Iʼm Kim Garcia. Iʼm your new classmate. These ar


きずなプロジェクト-表紙.indd


< D8291BA2E706466>

178 New Horizon English Course 28 : NH 3 1. NH 1 p ALT HP NH 2 Unit 2 p. 18 : Hi, Deepa. What are your plans for the holidays? I m going to visi

<4D F736F F D208BB38DDE5F F4390B394C52E646F6378>

産業構造におけるスポーツ産業の範囲に関する研究Ⅰ

There are so many teachers in the world, and all of them are different. Some teachers are quiet and dont like to talk to students. Other teachers like

jyoku.indd

are rational or not. This is rational. This part A can be expressed as the ratio of 2 integers. 1:54 aの 答 えは 有 理 数 です なぜかと 言 うと a で 求 められた 5 は 2 つの 整



(1) i NGO ii (2) 112


fx-9860G Manager PLUS_J

H24_後期表紙(AB共通)

,

A5 PDF.pwd

untitled

CONTENTS Public relations brochure of Higashikawa September No.755 2

Hospitality-mae.indd

<4D F736F F F696E74202D CEA8D758DC E396BC8E8C F92758E8C81458C E8C81458F9593AE8E8C>

紀要1444_大扉&目次_初.indd

CONTENTS Public relations brochure of Higashikawa November No.745 Higashikawa 215 November 2

untitled



Webサービス本格活用のための設計ポイント

Introduction Purpose This training course demonstrates the use of the High-performance Embedded Workshop (HEW), a key tool for developing software for


untitled

Warm Up Topic Question Who was the last person you gave a gift to? 一番最近誰にプレゼントをあげましたか? Special Topics2




David A Thayne Presents. Bonus Edition OK! tossa_h_ol.indd 1 12/12/07 21:09

SECTION TAXICABS IN TOKYO 2016

GOT7 EYES ON YOU ミニアルバム 1. ノハナマン What? I think it s stuck ノマンイッスミョンデェヌンゴヤ Yeah モドゥンゴルジュゴシポソ Yo baby ノワオディトゥンジカゴシポ everywhere ナンニガウォナンダミョンジュゴシポ anythin

日本看護管理学会誌15-2

CONTENTS Public relations brochure of Higashikawa February No.748 2

Transcription:

API Design Shawn M Moore Best Practical Solutions http://sartak.org Presented YAPC::Asia, 2009-09-10. Tokyo Institute of Technology, Tokyo, Japan.

Google API 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 At YAPC::NA 2009, this guy, Hans Dieter Pearcey aka confound aka hdp, presented a talk about Dist::Zilla. http://www.flickr.com/photos/nuffin/179250512/

Dist::Zilla was written by this other guy, Ricardo Signes, aka rjbs. http://www.flickr.com/photos/nuffin/179250812/ CC-BY-SA Yuval Kogman, 2006

CC-BY-SA Hans Dieter Pearcey, 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 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 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 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 Write tests.

WRITE TESTS WRITE TESTS WRITE TESTS WRITE TESTS WRITE TESTS Write so many tests your ears bleed. I am not joking!

DO THIS WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE If you remember nothing else, remember to write tests!

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! use base 'Class::Accessor::Fast'; PACKAGE ->mk_ro_accessors('birthday'); use Moose; has birthday => (is => 'ro'); API Write tests so you can tell if your API is painful to use. Which of these would you rather be stuck with?

Test!... then you'll piss your users off. They'll leave and use some other module, or worse, find out where you live.

Test! This is Jesse Vincent, the nicest guy in the world :)

Test!

Test!

Moose package Point; use Moose; has x => ( is => 'ro', isa => 'Num', ); Moose 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 difficult but it will get clearer and less heady, so wake up soon if you space out.

Metaobject Protocol Class::MOP Moose Class::MOP 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', ); 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 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 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 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 objectoriented programming!

Metaobject Protocol role PersistentAttr { } has cache => ( traits => ['PersistentAttr'], is => 'ro', ); 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 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 Moose also makes a very clean separation between its sugar layer and the rest of the system.

Sugar Layer my $class = get_class(); Say you wanted to get ahold of some class...

Sugar Layer my $class = get_class(); $class->has( birthday => ( is => 'ro', ) ); has 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 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', ) ); Not to mention how ugly this mess is.

Sugar Layer Class::MOP::Class ->initialize($class) ->add_attribute( $name, %options); has add_attribute 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', ) ); 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); } 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.

This is its sugar layer. Like Moose, it has a clean, extensible API if you want the freedom to do unusual things. 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 Path::Dispatcher is a standalone-uri dispatcher. I wrote it because I wanted Jifty::Dispatcher for Prophet's command-line interface.

Path::Dispatcher::Declarative use Sub::Exporter -setup => { exports => [ on => \&build_on, under => \&build_under,, ], }; Sub::Exporter 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,, ], }; 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 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(@_) },, }; 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 OO sugar is a really neat idea that I haven't seen anywhere else.

HTTP::Engine HTTP::Engine->new( interface => { module => 'ServerSimple', args => { }, request_handler => sub { }, }, )->run; HTTP::Engine HTTP::Engine abstracts away the various HTTP server interfaces that Perl has accumulated since HTTP was invented. The benefit is in letting the user pick which server interface best fits their particular needs.

HTTP::Engine HTTP::Engine->new( interface => { module => 'ModPerl', args => { }, request_handler => sub { }, }, )->run; mod_perl For example, you can use mod_perl if you enjoy pain.

HTTP::Engine HTTP::Engine->new( interface => { module => 'FCGI', args => { }, request_handler => sub { }, }, )->run; FastCGI Or FastCGI if you're a cool dude.

HTTP::Engine request_handler => sub { my $request = shift; return $response; } HTTP::Engine IO HTTP::Engine works well because the code you write doesn't have to worry about redirecting I/O streams, making sense of %ENV, or any of the other crap you do when writing against a particular server module.

HTTP::Engine request_handler => sub { my $request = shift; return $response; } HTTP::Engine OK HTTP::Engine boils the web server cycle to the least common denominator. You take a request...

HTTP::Engine request_handler => sub { my $request = shift; return $response; } and return a response.

HTTP::Engine++ 1 Can we please standardize on this? New server modules can implement an HTTP::Engine::Interface, then immediately every existing HTTP::Engine-based application can switch to it by changing only a single line of code.

Now I want to explain why this is so awesome. CC-BY-SA Hans Dieter Pearcey, 2009

Dist::Zilla AllFiles ExtraTests InstallDirs License MakeMaker Manifest ManifestSkip MetaYAML PkgVersion PodTests PodVersion PruneCruft Readme UploadToCPAN Here's a list of plugins used by a typical Dist::Zilla-based distribution.

Dist::Zilla AllFiles ExtraTests InstallDirs License MakeMaker Manifest ManifestSkip MetaYAML PkgVersion PodTests PodVersion PruneCruft Readme UploadToCPAN $_->gather_files for $self->plugins_with( -FileGatherer ); Dist::Zilla Dist::Zilla itself occasionally calls methods like this. The key bit is "plugins_with".

Dist::Zilla AllFiles ExtraTests InstallDirs License MakeMaker Manifest ManifestSkip MetaYAML PkgVersion PodTests PodVersion PruneCruft Readme UploadToCPAN plugins_with takes a role name... $_->gather_files for $self->plugins_with( -FileGatherer ); plugins_with

Dist::Zilla ExtraTests InstallDirs MakeMaker Manifest ManifestSkip PkgVersion PodVersion PruneCruft UploadToCPAN AllFiles License MetaYAML PodTests Readme $_->gather_files for $self->plugins_with( -FileGatherer );...and selects the plugins that "do" the role. These plugins all do the "FileGatherer" role, which means the plugin adds files to a distribution.

Dist::Zilla ExtraTests InstallDirs MakeMaker Manifest ManifestSkip PkgVersion PodVersion PruneCruft UploadToCPAN AllFiles License MetaYAML PodTests Readme $_->gather_files for $self->plugins_with( -FileGatherer ); gather_files Then, dzil calls gather_files on each of these plugins so it can actually add files to the distribution. "License", "Readme", and "MetaYAML" add the respective files, "AllFiles" adds every file the author wrote. "PodTests" adds pod testing files to the distribution.

Dist::Zilla AllFiles License MakeMaker Manifest ManifestSkip MetaYAML PodTests PruneCruft Readme UploadToCPAN ExtraTests InstallDirs PkgVersion PodVersion $_->munge_files for $self->plugins_with( -FileMunger ); Dist::Zilla Dist::Zilla uses this architecture for all of the interesting parts of building a CPAN distribution. This is "munging files", which lets plugins edit files to increase the version number, or move tests around.

Request Tracker User/Prefs.html $m->callback( CallbackName => 'FormEnd', UserObj => $UserObj,, ); RT It turns out that RT has a very similar extension mechanism.

Request Tracker User/Prefs.html $m->callback( CallbackName => 'FormEnd', UserObj => $UserObj,, ); This code exists in User/Prefs.html. The callback method selects all plugins that do the "User/ Prefs.html" "role".

Request Tracker User/Prefs.html $m->callback( CallbackName => 'FormEnd', UserObj => $UserObj,, ); FormEnd Then it calls the FormEnd "method" (template) on these selected plugins.

Request Tracker User/Prefs.html $m->callback( CallbackName => 'FormEnd', UserObj => $UserObj,, ); And you can pass arbitrary parameters to each method.

Request Tracker User/Prefs.html $m->callback( CallbackName => 'FormEnd', UserObj => $UserObj,, ); This works extremely well for us! We try to build most customer extensions with callbacks. It's basically the same design as Dist::Zilla's.

Request Tracker commit 4c05a6835eef112701ac58dfd1b133e220059d4f Author: Jesse Vincent <jesse@bestpractical.com> Date: Fri Dec 27 18:50:06 2002-0500 Attempting mason callouts Ticket/Update.html <& /Elements/Callback, Name => 'BeforeTextarea', %ARGS &> 7 RT has had callbacks since 2002, first released in 3.0.0. This pattern has been the best mechanism for any kind of RT extension for almost seven years now.

Dist::Zilla Choice ModuleBuild MetaYAML or or MakeMaker MetaJSON This design gives the user choice over which behavior she wants. And in my experience, users really really want choice.

Dist::Zilla Extensibility Dist::Zilla::Plugin::CriticTests Dist::Zilla::Plugin::Repository Dist::Zilla::Plugin::PerlTidy This design is also extensible for free. These are some of the modules that have been written to extend Dist::Zilla.

Dist::Zilla Extensibility Dist::Zilla::Plugin::CriticTests InlineFiles Dist::Zilla::Plugin::Repository MetaProvider Dist::Zilla::Plugin::PerlTidy FileMunger All they need to do is fulfill the requirements of the roles they "do". I'm going to talk about that more in my (Parameterized) Roles talk. http://sartak.org/talks/yapc-asia-2009/(parameterized)-roles/

Dist::Zilla Extensibility Dist::Zilla::Plugin::BPS::Secret Extensibility is also important for code you can't share. We can't ask Ricardo to include company secrets for Dist::Zilla, and maintaining a fork really sucks.

So now you know! CC-BY-SA Hans Dieter Pearcey, 2009

IM::Engine incoming_callback => sub { my $incoming = shift; my $message = $incoming->plaintext; $message =~ tr[a-za-z][n-za-mn-za-m]; } return $incoming->reply($message); IM::Engine IM::Engine is a project I'm working on. It's basically HTTP::Engine for IM. You can write a bot, once, that will run on any service IM::Engine can talk to, including IRC. IM::Engine smooths over the differences in the protocols.

IM::Engine $self->plugin_collect( role => 'ExtendsObject::User', method => 'traits', ); plugin_collect I've extended Ricardo's design with a number of helper methods. plugin_collect is the one I like best.

IM::Engine $self->plugin_collect( role => 'ExtendsObject::User', method => 'traits', ); For each plugin that does the ExtendsObject::User role...

IM::Engine $self->plugin_collect( role => 'ExtendsObject::User', method => 'traits', ); traits...call its "traits" method.

IM::Engine my @all_traits = $self->plugin_collect( role => 'ExtendsObject::User', method => 'traits', ); The return value of this call is the list of all return values of the "traits" methods.

method plugin_collect { my @items; $self->each_plugin( callback => sub { push @items, shift->$method }, ); } return @items; plugin_collect This is the important part of plugin_collect's implementation. There's not much there. I like very layered APIs because they're easier to understand and reuse, especially by your users, than huge monolithic methods. Each layer does only a little bit of work.

IM::Engine method new_with_plugins { my %args = ( $self->plugin_collect( role =>, method => 'ctor_args', ), @_, ); $self->new(%args); } Here's a piece of design I like a lot. This lets plugins participate in object construction. Each plugin can provide constructor arguments.

IM::Engine push @{ $args{traits} }, $self->plugin_collect( role =>, method => 'traits', ); $self->new_with_traits(%args); This lets plugins participate even more in object construction. Now plugins can provide roles for the object you're constructing. This lets plugins add attributes and methods to the object. I use this in a plugin to give state management methods to User objects.

MooseX::Traits $object = Class->new_with_traits( traits => ['Counter'], ); $other = Class->new; $object->counter; # 0 $other->counter; # Can't locate... new_with_traits MooseX::Traits new_with_traits comes from MooseX::Traits. It's a really nice module for designing pluggable and extensible systems. You just pass a list of roles to new_with_traits and it will arrange it so that the object does those roles.

MooseX::Traits $object = Class->new_with_traits( traits => ['Counter'], ); $other = Class->new; $object->counter; # 0 $other->counter; # Can't locate... Other objects of that class are not affected by new_with_traits. The way it works internally is by creating a new subclass of Class. This is vital because it maintains modularity. I don't want my extensions to screw up your extensions.

Role = Trait Moose Role Trait In Moose land, roles and traits are basically synonymous. Some people will tell you there are subtle differences, but there's no clear consensus. I just say "roles" except when I have to say "traits" for a module.

Moose Path::Dispatcher HTTP::Engine Dist::Zilla IM::Engine So that is all I have time to cover. There are plenty more nice examples in modules like KiokuDB, Fey, and the now-moosified Catalyst.

Moose Extensibility Separation of sugar Dist::Zilla IM::Engine Moose Moose teaches us that extensibility can lead to a great corpus of extensions. Separation of sugar keeps you and your users flexible.

Moose Path::Dispatcher OO sugar layer Dist::Zilla IM::Engine OO The OO sugar layer is a new idea that I hope catches on. I'll have to dedicate more time to it.

Moose Path::Dispatcher HTTP::Engine Omit inconsequential details IM::Engine If you omit inconsequential details, then your application remains flexible and concise.

Moose Path::Dispatcher HTTP::Engine Dist::Zilla Explicit pluggability Pluggability does not have to be implicit, as in subclassing. Explicitly controlling pluggability lets you do more interesting things.

Moose Path::Dispatcher HTTP::Engine Dist::Zilla IM::Engine Extreme pluggability DRY IM::Engine DRY such as the things IM::Engine does, by letting plugins manipulate system objects. It also provides methods for common plugin operations so you don't have to repeat them everywhere.

WRITE Moose Path::Dispatcher HTTP::Engine Dist::Zilla TESTS!!! IM::Engine I almost forgot...

Moose Path::Dispatcher HTTP::Engine Dist::Zilla IM::Engine I almost forgot...

Thanks to my translator Kenichi Ishigaki Thank you to Ishigaki-san for translating my slides! 83