SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

よろずプログラマーのためのPython導入ガイド

Java meets Python - 第2回 配列と別れる50の方法

よろずプログラマーのためのPython導入ガイド (4)

ビューの構成

 見た目がすべてではありませんが、やはり第一印象は大切にしたいものです。

理想的な上司

 親クラスには、理想的な上司のような存在であって欲しいものです。

class DefaultFrame(JFrame):
    defaultTitle = "Default Title"
    defaultSize = Dimension(200, 100)

    def __init__(self, title=None, size=None):
        self.initialize()
        self.initializeFrame(title, size)
        self.initializeComponent()
        self.visible = True
    def initialize(self): pass
    def initializeFrame(self,
            title=defaultTitle, size=defaultSize):
        JFrame.__init__(self,
            defaultCloseOperation=JFrame.EXIT_ON_CLOSE,
            title=title, size=size)
    def initializeComponent(self): pass

 クラスDefaultFrameは、抽象クラス(理想)として、子孫クラスに共通する特性を規定します。メソッド__init__は、そのテンプレートとして、共通するフレームワークだけを規定します。ここで、initialize/initializeComponentは、抽象メソッドとして、子孫クラスで『必要なら』再定義することを想定しています。メソッドinitializeFrameは、ウィンドウのタイトルに指定する文字列titleと、大きさ(幅/高さ)sizeを設定します。ただし、実際の処理は、親クラスJFrameで規定したJFrame.__init__に委ねます。

 ここで着目して欲しいのは、親/子孫クラスの役割分担です。DefaultFrameでは、役割分担を規定するだけで、作業には介入しません。必要な要員/予算だけ確保して、現場には口を出さない、理想的な上司を演じます。ただし、名前Default..から受ける印象とは違って、暗黙の規定値(デフォルト)にはならないので、子孫クラスでは「親の七光に肖りたい」という意思を表明する必要があります。すると、何か問題があれば親が事態を収拾して、何か功績があれば子孫の栄誉となります。

理想的な部下

 子孫クラスには、理想的な部下のような存在であって欲しいものです。

class Puzzle15Frame(DefaultFrame):
    def __init__(self, title=None, size=None):
        DefaultFrame.__init__(self, title, size)
    def initialize(self):
        self.panel = PuzzlePanel()
    def initializeComponent(self):
        self.layout = BorderLayout()
        self.add(self.panel)

 クラスPuzzle15Frameは、具象クラス(現実)として、親クラスDefaultFrameで規定した特性を再定義したり、実質的な作業を他に委ねたりします。

 メソッド__init__では、実引数title/sizeを、DefaultFrame.__init__に委ねるだけで、自分は何もしません。これは、既存のフレームワークに従うことを宣言したことになります。つまり「自分は何もしない」ことに意義があるのです。

 メソッドinitializeでは、そのクラスに固有の特性を規定します。インスタンス属性panelは、フレーム内に配置するパネルPuzzlePanelを保持します。

 メソッドinitializeComponentでは、ビュー(視覚部品)の特性を規定します。指定したレイアウトBorderLayoutに従って、パネルを追加addします。

 ここで着目して欲しいのは、親クラスの規定に従うなら何をしても自由ですが、それに従わないならすべて自己責任となることです。Puzzle15Frameの責務には、上司の指示を仰ぐ行為も含まれます。このパズルの成否はすべて、部下Puzzle15Frameの自主的な活動次第となります。

理想的な作業机

 同じ作業机を与えても、そこには個性が反映されます。机の上を整理整頓しておくと作業がはかどるように、部品の構成管理にも配慮したいものです。

class PuzzlePanel(JPanel):
    dim = 4
    dim2 = dim*dim
    def __init__(self):
        self.initialize()
        self.initializeComponent()
        self.shuffle(1000)
    def initializeComponent(self):
        self.mouseClicked = self.this_mouseClicked

 クラスPuzzlePanelは、ビューを構成する部品の一つとして、アプリケーションに固有の機能を提供します。

 インスタンス属性dim/dim2は、すべてのインスタンスに共通する情報(定数項)を規定します。ただし、要求仕様に変更があると、この箇所が問題となるでしょう。

 メソッド__init__では、親クラスJPanelで規定したプロトコルに従って、パネルを再構成します。initializeは、そのクラスに固有の特性を規定するもので、先に示した方法でパネル内にコマを配置します。shuffleは、指定した1,000個の乱数を発生させて、それをもとに任意のコマを移動させます。

 メソッドinitializeComponentでは、ビュー(視覚部品)の特性を規定します。インスタンス属性mouseClickedは、イベントハンドラとして、関数オブジェクトthis_mouseClickedを保持します。

理想的な文房具

 同じ文房具を与えられても、作業がはかどるかどうかは、その使い方次第です。

class Shape:
    def paint(self, g):
        raise NotImplementedError, "def paint(self,g)"

 クラスShapeは、インターフェースとして、子孫クラスに共通するプロトコルを規定します。メソッドpaintは、子孫クラスで『必須として』再定義する必要があり、そうしないと、実行時に例外NotImplementedErrorを生成します。

フレームワークの意義は再利用だけではない
 Pythonにおける抽象クラス/インターフェースは、Java/C#におけるabstract/interfaceと違って、コンパイラによって強制されるものではありません。規則に縛られず、プログラマが慣習に従って自主的に用います。この関係を「強制と共生の文化の違い」と評する人もいます。paintでは、例外NotImplementedErrorを生成することで、プログラマの注意を促し、作業プロセスが適切に遂行されることを期待します。洗練されたフレームワークでは、作業成果(プロダクト管理)と、作業過程(プロセス管理)とは、コインの裏表と同様に統合されています。プロダクト指向からプロセス指向への扉を開く話題については、後の連載で紹介します。
class Tile(Shape):
    def paint(self, g):
        self.paintBackground(g);
        self.paintItem(g);
    def width(self, g):
        return g.clipBounds.width/PuzzlePanel.dim
    def height(self, g):
        return g.clipBounds.height/PuzzlePanel.dim

 クラスTileは、インターフェースShapeの規定に従って、メソッドを再定義します。

 メソッドpaintは、各コマの背景paintBackgroundと前景paintItemを適切に描画します。

 メソッドwidth/heightは、パネルの大きさが変化しても、各コマの大きさ(幅/高さ)を適切に保つために必要な情報を提供します。

絶景かな?

 頂上から眺めると、見渡す限りの風景の中に、そこにたどり着くまでの道程も見えてきます。ソフトウェア開発でも、完成したプロダクトを眺めるだけでなく、そこにたどり着くまでのプロセスを俯瞰してみると、そこに新たな世界が開けて見えてきます。

 PuzzlePanelは、Tileではなく、インターフェースを規定したShapeに依存しています。PuzzlePanelが保持するコマは、Tileだけとは限りません。Shapeで規定したプロトコルに従うなら何でもかまわないので、より柔軟性に優れたコードを記述できます。この話題については、後の連載で紹介します。

 ここで着目して欲しいのは、Shapeを境に、表示の対象となるモデルと、表示を担当するビューとを分断したことです。モデルの立場からは、より見栄えのする表示をしたいものです。ビューの立場からは、同じボードを使って他のゲームを楽しみたいものです。この考え方は、MVCなどのフレームワークを構築するときにも役立ちます。

山手線の起点/終点(解決編)
 線路名称としての「山手線」の起点は品川駅、その終点は田端駅になるそうです。また、運転系統としての「山手線」は、東京都区内で環状運転を行っています。学生時代には、黄緑色のラインで象徴される、山手線を幾度となくグルグルと回ったものです。春休みの余暇を利用して、沿線の風景を眺めつつ、各駅停車の心地よい揺れ具合は、読書の場としては最高でした。ところで、品川駅は品川区にはなく、港区にあるのを知っていますか。また、関東にあるのは「やまのてせん」と呼ばれますが、関西にあるのは「やまてせん」と呼ばれます。だから何なの…と言われそうですが、それも一興です。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
よろずプログラマーのためのPython導入ガイド連載記事一覧

もっと読む

この記事の著者

小泉ひよ子とタマゴ倶楽部(コイズミヒヨコトタマゴクラブ)

http://tamago-club.cocolog-nifty.com/「楽しくなければ仕事じゃない」が私たちのモットー。99%の苦悩の連続も、1%の成功に報われます。だからこそ、この仕事が楽しくて仕方がないのです。楽をするための努力なら惜しみません。何もせず楽をしているのと、努力をしたから楽ができるのと...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

伊藤うさぎ(イトウ ウサギ)

ペンネームの「由来は」と言うと。苗字の方は、セミナー研修で同じチームになった、3人の合体ユニット名 [I:石塚, T:田川, O:尾沢] から来ています。名前の方は、同じ干支(卯:1987 年生)に因んだものです。既に2人は卒業して、残る1人がその名跡を継承しています。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1860 2007/11/13 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー