はじめに
MVCP: Model, View, Controller, Presenterの翻訳です。
業務でMVCPに触れておきながら、きちんと理解していないと感じ、少しググったところ手頃なドキュメントを見つけたので、翻訳してみることにしました。(誤訳していたらスミマセン。筆者の英語翻訳能力は高くはないです。)
英語の翻訳練習も兼ねています:)
Introduction - はじめに
Model, View, Controller (MVC). This is a pretty standard architectural pattern and has been in use when developing software since the early 1970's.
Model, View, Controller (MVC)。これは標準的なアーキテクチャパターンであり、1970年代初頭からソフトウェア開発する際に使われてきた。
The basic principle of the pattern is to separate the different areas of logic from your application into distinct compartments.
そのパターンの基本原則は、アプリケーションから異なるロジックの領域を明確なコンポーネントに分離することだ。
Model
The model holds your business data. Typically this will be data that is pulled in from a database or external data service of some kind.
model はビジネスデータを保持する。典型的には、データベースやある種類の外部サービスデータから引っ張ってくるデータだ。
View
The view is your user interface. This is what the client will interact with when using your application.
view はあなたのユーザインタフェースである。これはアプリケーションを使う際に、クライアントが相互作用する何かだ。
Controller
The controller is the boss. He sits at the top and delegates responsibilities to either the view or the model.
controller は、ボスである。彼は最上位に存在し、責任を view や model に委譲する。
Mixed definitions - 矛盾した定義
There seems to be a dispute in the dev community regarding how the responsibilities should be divided.
開発コミュニティではどのように責任を分割すべきかに関して論争になるようだ。
Some feel a 'fat controller' principle is best (where by the controller tells the model not only when but where and how it should get its data).
何人かは '太ったcontroller' 原則がベストだと感じる(それによって controller は model にデータをいつ、どこに、そしてどのように取得すべきかを伝える)。
My understanding of the pattern is that it was designed so that the Controller stays 'skinny'. It may be the boss, but like most good bosses it doesn't try and stay in control. It knows the best team member for the job at hand and delegates accordingly.
私は、Controller は 'skinny' にとどまるよう設計されたと考えている。それはボスかもしれないが、支配しようとはしない大部分の良いボスだ。そのボスは、仕事に対して最も良い身近なチームメンバーを知っていて、適切に委譲する。
This is also good code design because the Controller doesn't have too much context (i.e. it doesn't know everything, which means it'll be easier to maintain and scale).
Controller は多くのコンテキストを持っていないので、これもまた良いコード設計だ(すなわち、Controller は全てを知っておらず、それが意味するのは保守やスケールがし易いことだろう)。
God Controller - ゴッドコントローラー
There are a few ways we can implement an MVC pattern, one is known as the 'God Controller'.
MVCパターンを実装する方法はいくつかあり、そのひとつは 'ゴッドコントローラー' として知れている。
This is where a single Controller exists and it oversees everything no matter what was requested by the client.
これは単一の Controller が存在し、かつクライアントにリクエストされたものが何であろうと全て監視する。
For example, the single Controller would be passed the request from the client (usually handled by a custom routing application, and most frameworks will provide their own).
例えば、単一の Controller はクライアントからリクエストを渡されるかもしれない(通常、カスタムルーティングアプリケーションによって扱われ、多くのフレームワークは自身で提供している)。
The Controller would determine what type of request was made (if the request was for a 'contact' page then it'll make a request for the Contact model, or if the request was for the 'about' page then it'll make a request for the About model).
Controller はリクエストの種類が何で構築されたかを決定するかもしれない(リクエストが 'contact' ページに対してであった場合、Contact model に対するリクエストを作成するだろうし、'about' ページに対してであった場合、About model に対するリクエストを作成するだろう)。
Once it knows the type of request it'll proceed to get the relevant model data and assign it to some View variables and render the required View.
一旦リクエストの種類を知ると、関連するモデルデータの取得を開始し、そのデータをいくつかの View 変数に割り当て、必要とされる View をレンダリングする。
Problems - 問題
Now there are two problems with this implementation:
さて、この実装には2つの問題がある:
- maintainability
-
scalability
-
保守性
-
スケーラビリティ
As mentioned before, this comes down to bad code design. The 'God Controller' knows too much and tries to do too much. Once you start getting above a few different types of requests you'll start to understand what a mess the code can become by having multiple branches for different routing scenarios.
前述した通り、これは悪いコード設計に成り下がる。'ゴッドコントローラー' は知り過ぎであり、かつ、やろうとし過ぎである。一旦上述のいくつかの異なるリクエストの種類を取得すると、ルーティングシナリオに対し複数の分岐を持つことにより、大量のコードがどうなるかを理解し始める。
I work as an engineer for the BBC News team in London and we had suffered from this exact set-up (hence the lessons the team has learnt and improved upon are the reason why I'm able to write this post for you now).
私はロンドンでBBCニュースのチームエンジニアとして働いており、まさにこのようなセットアップの被害を受けてきた(それ故、チームが学び、かつ改善した教訓が、私がこうしてあなたにこの投稿を書き得る理由だ)。
Skinny Controller - スキニーコントローラー
There is another approach we can take which is known as the 'skinny controller' approach.
他に取ることができるアプローチには、'スキニーコントローラー' アプローチとして知られるものがある。
The way it works is that a request will come into the application and will get passed to a page specific Controller.
その動作は、リクエストがアプリケーションにやってくると、ページ特有の Contller に渡すことである。
The page specific Controller will call the relevant Model and will assign the returned data to a few View variables.
ページ特有の Controller は関連した Model を呼び出し、いくつかの View 変数に返却されたデータを割り当てる。
The Controller will then render a View and pass through the variables into the View for it to use.
続いて、Controller は View をレンダリングし、そして View が使う変数を渡す。
As you can see, this isn't that different from the 'God Controller' with the exception that the Routing part of the application now will have extra logic which determines which specific Controller should be loaded. This is a better situation to be in because you're making your code base both more maintainable and scalable.
ご覧の通り、これはアプリケーションのルーティング部分が今ではどの特有の Controller を読むべきかを決定するという余分なロジックを持つことを除いて、'ゴッドコントローラー' とそれほど違いがない。これは良い状況だ。何故なら、あなたはあなたのコードベースを保守しやすく、かつスケールしやすくしているからだ。
Note: as I mentioned in the previous section, BBC News had a sort of 'God Controller' issue and our first step to resolving the problem was to take a similar approach as described above (i.e. to start creating page specific Controllers). That was a good first step.
注記: 前セクションで述べた通り、BBCニュースは一種の 'ゴッドコントーローラー' 問題を持っていて、問題を解決する最初の一歩は上述で述べたものと似たアプローチを採ることだった(すなわち、ページ特有の Controller を作成し始めること)。これは良い最初の一歩であった。
The next step from here was to separate out our logic even further by implementing Presenters, and it was our tech lead at BBC News (John Cleveley) who made that decision which resulted in a much more efficient, maintainable and scalable code base.
ここから次へのステップは Presenter を実装することにより、さらにもっとロジックを分離することであり、そして、もっとより効率的で、保守性のある、スケーラブルなコードベースになる決定を下したBBCニュースでの我らがテックリード(John Cleveley)である。
Presenters
What problem are Presenters trying to solve? - Presenter が解決しようとする問題は何か?
Let's imagine we've gone for the 'Skinny Controller' approach. There are still some inherent issues…
'Skinny Controller' アプローチで実装してきたと想像しよう。なお先天的な問題がある。
First of all, our Controller can still have too much context and be handling more information than it should.
最初に、Controller はコンテキストを持ち過ぎている可能性があり、本来よりも多くの情報を扱っている可能性もある。
But also, and more importantly, you may find there is still a lot of duplication of code across your Controllers.
しかしより重要なことに、Controller 間で多数のコードの重複があることに気づくかも知れない。
The reasoning for this is that if you consider the structure of a web page/application you'll notice that it is typically made up of unique 'features'. For example, if you're displaying your tweets on a page then that's a unique feature.
この理由は、web ページ/アプリケーションの構造を考慮していると、それは典型的に唯一な'機能'により作られていることに気づくからだ。例えば、ツイートをページに表示しているとすれば、それは唯一の機能だ。
Each feature must be able to stand on its own. We normally describe these features as being 'components'. Each component can be loaded whenever and wherever needed. Having a component based architecture allows your code base to become more modular and reusable.
各機能は、それ自身に立脚していなければならない。通常、これらの機能は 'components' と記述する。各コンポーネントはいつでもどこでも必要とあれば読み込まれる可能性がある。コンポーネントに基づいたアーキテクチャを持つことは、コードベースのモジュール性、再利用性をより高める。
For example the navigation menu on a page could be considered a 'component'. Also, the navigation menu component is likely going to need to appear on every single page of the application.
例えば、ページ上のナビゲーションメニューは 'component' と考えられ得る。また、ナビゲーションメニューはアプリケーションのシングルページ毎に現れることになるかもしれない。
So, if you're splitting up your logic into page specific Controllers then it's possible that you're still repeating code across the Controllers to handle the loading of re-occurring components such as the navigation (e.g. pulling its data from a navigation Model and setting View variables etc).
なので、ロジックをページに特有な Controller に分割していると、複数のControllersに跨ってコードを繰り返す可能性があり、それらのControllersはナビゲーションのような再発生するコンポーネントの読込みを扱うために使われる (すなわち、navigation Modelからデータを引き出し、View変数に送る、など)。
Now there are ways that this code repetition can be avoided, and one such way is to use the concept of Presenters.
さて、このコードの繰り返しを避け得る方法があり、そしてそのような方法の1つは Presenters のコンセプトを使うことだ。
How do they work? - どのように働くか
Presenters (like everything in software engineering) can be implemented in many different ways.
Presenter は(ソフトウェアエンジニアリングにおける全てと同様に)、多数の異なる方法で実装され得る。
For example, at BBC News we initially were manually creating new Presenter instances
within our page Controllers. But the team here are quite clever chaps
(especially Robert Kenny and Simon Thulbourn) and they realised that this process could
be greatly improved by using configuration files instead (specifically YAML).
As we have multiple teams working on the BBC News code base and in multiple languages,
using configuration files is a much easier and maintainable solution.
例えば、BBCニュースでは最初は page Controller で新たな Presenter を手動で生成していた。
しかし、チームは以下に示すとても賢い人たち(特に Rovert Kenny と Simon Thulbourn)、 彼らはこのプロセスをコンフィグファイル(特にYAML)を代わりに使うことではるかに改善され得ることを悟った。
BBCニュースのコードベース上で、複数の言語で働いている複数のチームがあるので、コンフィグファイルを使用することは、より簡単で保守し易いソリューションである。
I'm not going to go into the configuration set-up we use at BBC News.
Instead I'll focus on the basic principles of how Presenters work,
which is quite simply a case of moving the logic (getting component specific Model data and
assigning it to to component specific variables) into separate files called Presenters
which you can instantiate within your controller.
BBCニュースで使用しているコンフィグファイルによるセットアップについては述べない。
代わりに、Presenter がどのように働くかの基本原則に焦点を当てる。
その基本原則は、controller で実体化できる Presenters と呼ばれる分離されたファイルにロジック(コンポーネント特定の Model データを取得し、それをコンポーネント特定の変数にそれを割り当てる)を移すとてもシンプルな例だ。
Code Example
Controller
Here is a basic example in Ruby…
これは Ruby における基本的な例である。
require 'app/presenters/a'
require 'app/presenters/b'
class AboutController < ApplicationController
get '/' do
@a = Presenters::A.new
@b = Presenters::B.new
title 'About'
erb :about
end
end
…in this example we have an 'About' page which is made up of two components a and b. As you can see we require the presenters which handle those two components and within our Controller we instantiate the Presenters.
この例では 2つのコンポーネント a
と b
で作られた 'About'
ページを持つ。ご覧の通り、これらの2つのコンポーネントを扱う presenters を必要とし、Controller で Presenters を実体化する。
Notice that's all we do. Each Presenter encapsulates the logic needed to prepare
the data to be passed to the :about view template.
これがやっていること全てであることに注意。各 Presenter は :about
view テンプレートに渡されるデータの準備を必要とするロジックをカプセル化する。
View
Before I show you the Presenter code itself, I'll show you the View template file…
Presenter のコードを見せる前に、View テンプレートファイルを見せよう。
<h1><%= @title %></h1>
<% if @a.run %>
<%= partial :"components/a", { :title => @a.title, :summary => @a.summary, :data => @a.data } %>
<% end %>
<% if @b.run %>
<%= partial :"components/b", { :name => @b.name, :age => @b.age } %>
<% end %>
…as you can see we have very minimal logic in place. If anything I have too much logic in the View as I initially was re-using the same View template over and over and so I wanted to protect again errors appearing when loading a template which referenced a component I wasn't loading, but I've since changed how my application was working but left the conditional checks in as an example of how code can evolve over time.
ご覧の通り、とても小さなロジックが適切にある。最初は同じ View テンプレートを繰り返し再利用しており、未ロードのコンポーネントを参照したテンプレートをロードすると発生するエラーを防ぎたかった等の理由で View でたくさんのロジックを持たせると、時間の経過とともにどのくらいコードが発達し得るかの例の通り、条件チェックを置き去りにしてアプリケーションの挙動を変更してしまった。
We literally just check to see if the component has been initialised (in this case
we created a run property we set to true when the component's Presenter is first initialised).
コンポーネントが初期化済みかを単に文字通り確認する(この場合、コンポーネントの Presenter が最初に初期化されるときに true に設定される run プロパティを生成した)。
We then render the View for the component and pass through the variables that were set-up from within the Presenter.
そして、component に対し View をレンダリングし、Presenter から渡された設定済み変数を渡す。
Now I can also open up my :home View file and add in the a component there as well just as easily.
It would be even easier if I didn't have to manually add the a component to the :home View file
but that's where running from configuration files like we do at BBC News would come in handy
(but that would have been too complicated an implementation for the sake of such a
basic example as required for this post).
さぁ :home
View ファイルもまた開発でき、そこにコンポーネントを簡単に追加できるようになった。
便利になるであろうBBCニュースでやっているのと同様に、コンフィグファイルから実行している :home
View ファイルにコンポーネントを手動で追加する必要がないと、より簡単になる(しかしそれは、この投稿に必要な基本的な例のような目的に対して実装が複雑過ぎであっただろう)。
Presenters
Now let's take a look at one of our Presenters, in this case the Presenter for our b component…
さて、Presenter の 1つを見ていこう。ここでは b コンポーネンに対応する Presenter の例である。
require 'app/presenters/base'
require 'app/models/b'
class Presenters::B < Presenters::Base
attr_reader :run, :name, :age
def initialize
@run = true
model = B.new('Mark', '99')
prepare_view_data({ :name => model.name, :age => model.age })
end
end
…as you can see we load a specific Model for this component and then generate our View data by
passing the Model information through to a prepare_view_data method (see below for the implementation details).
ご覧の通り、このコンポーネントに対応する特定の Model を読み込み、Model 情報を prepare_view_data メソッドに渡すことで View データを生成する(詳細な実装は下記を参照のこと)。
The Base Presenter which our component Presenters inherit from is very straight forward as
you can see from the following example…
コンポーネントの Presenters の継承元である Base Presenter は、下記の例から分かる通り、理解がとても容易である。
module Presenters
class Base
attr_accessor :model
def prepare_view_data hash
hash.each do |name, value|
instance_variable_set("@#{name}", value)
end
end
end
end
…it's just a module namespace with a base class that has a single method prepare_view_data which
dynamically generates instance variables based on the data we passed through from the
inheriting Presenter class and which then are usable within the View.
継承先 Presenter クラスから渡されるデータに基づくインスタンス変数を直接生成し、かつ View で使用可能な prepare_view_data メソッドを1つだけ持つ基底クラスを含む、単なるモジュール名前空間である。
Conclusion - 結び
That's all there is to it as far as understanding the Presenter pattern. It's a nice clean solution for componentising your different page features and keeping your code more easily maintainable.
Presenter パターンを理解する限りでは、これで全部だ。
Presenter パターンは、異なるページ機能のコンポーネント化に対する明確で良いソリューションであり、コードの保守がより簡単になる。
I've created a repo on GitHub called MVCP which is written in Ruby and uses the Sinatra web framework. Note: I had some help from my colleague Simon in cleaning up and refactoring some of the code (it may only have been minor changes but as with all good refactorings it made a massive difference to the quality of the code, so thanks to him for helping out).
GitHub に Ruby で書かれ、Sinatra web framework を用いた MVCP というリポジトリを作った。
注意: 同僚の Simon にコードのリファクタリングとクリーンアップをいくらか助けてもらった(マイナーチェンジに留まったかもしれないが、全ての優れたリファクタリングと同様に、コードの品質が劇的に変わった。彼らの助けに大変感謝する。)
If you have any questions then feel free to contact me either here on twitter and let me know your thoughts.
もし質問があれば、こちらか twitter で気軽に連絡していただき、あなたの考えを教えてください。
訳者 結び・謝辞
もし質問があればご気軽に、ということだったので、Twitter で著者の @integralist さんに、この記事を翻訳して良いか尋ねたところ、快諾して頂きました。Many thanks:)
@integralist さんの記事は下記から参照できます。