連載
.NETで始めるデザインパターン

第1回 .NET開発におけるデザインパターンの有用性

太陽システム株式会社 中西 庸文
2005/01/12
Page1 Page2


Back Issue
1
.NET開発におけるデザインパターンの有用性
2
うまくデザインパターンを使うための心得
3
リファクタリングにより導き出すStrategyパターン
4
リファクタリングにより導き出すTemplate Methodパターン
5
Compositeパターンを導き出すための準備
6
リファクタリングにより導き出すCompositeパターン
7
デザインパターンの落とし穴

 「この処理は、確か前に担当したプロジェクトで作ったものとよく似ているな」

 開発者なら誰しもこのような局面にたびたび遭遇するものだ。さぁ、あなたならどうする?

 まずは以前のプロジェクトのソース・コードを流用することを考えるだろう。過去のプロジェクトのソース・コードから対象となるクラスやメソッドを探し出し、現在のソース・コードにコピー&ペーストして修正を加えるだけで、昔のプロジェクトのコードを再利用できる。しかも昔のプロジェクトのコードは、安定稼働している実績あるコードだから信頼性もある。

 昔のコードをそのままコピー&ペーストできないとしても、そのクラス構造を再利用できる場合は多い。過去に考え出したクラス構造のパターンを抜き出して、その中身を書き換えるというやり方だ。もしそれらのクラスがうまく抽象化できていれば(たいていの場合そんなことはないのだが)、差分をコーディングするだけでよいかもしれない。

 だが果たして取り得る解決策はこれだけだろうか。

 あなたのその「オブジェクト指向設計において、過去に同じような問題を解決した」というノウハウ――つまり、「私だけが知っている」とあなたが信じて疑わないその秘密のノウハウ――が、もうすでに多くの熟練した開発者たちによって発見され、文書化されているとしたら? また、あなたが将来遭遇するかもしれない多くの問題に対する解決方法も、すでにカタログ化されているとしたら?

 真実を語ろう。あなたが握っているその秘密の問題解決の知恵は、オブジェクト指向設計に深くかかわってきた先人たちによりすでに文書化されている。経験則やソース・コードという形ではなく、さまざまなシステムに組み込めるような形で、しかも開発言語に依存しない形でまとめられているのだ。それが「デザインパターン」だ。

 それでもあなたは、デザインパターンの有効性を疑うかもしれない。「確かにデザインパターンはJava開発ではポピュラーだという話は聞く。だけど、.NET開発では役立たないのでは?」「そもそも、デザインパターンを用いた設計が、すでに実践で使われているソース・コードやクラス構造を再利用することよりも優れているのか?」と思っている読者も多いだろう。

 そこで本稿ではまず、簡単な開発例を通して、.NETの開発でデザインパターンを適用すると、そのシステム設計がどのように改善されるかを示そう。これにより、デザインパターンの有効性を証明する。そして次回以降、具体的にデザインパターンとは一体どのようなものなのかについて簡単に紹介していくことにする。なお、本稿の開発手順はすべてテスト駆動開発(TDD:Test-Driven Development)のスタイルで行うつもりだ(テスト駆動開発については、「テスト駆動開発ツール最前線」を参照してほしい)。

.NETにおけるデザインパターンの必要性

 最初にデザインパターンを適用していない場合の設計について考えてみよう。

●デザインパターンが適用されず、問題を含んでいる設計の例

 ここでは、開発中のシステムに組み込まれる、以下のような要件を満たすサブシステムを実装するものとする。

  1. 開発者から、次のモチベーション指数を取得できる
     「どのくらいテストが好きか」
     「どのくらいリファクタリングを行っているか」
     「どのくらいソフトウェア開発を楽しんでいるか」

  2. 開発者には次の種類が存在する
     「一般的な開発者」
     「アジャイルな開発者」

 これらの要件を持つシステムを作るために、この内容をクラスにしてみよう。

 まずはブレイン・ストーミングを行い、ホワイト・ボードにクラス図をさっと書いてみる。

 次の図は、ホワイト・ボードに書き出された「開発者」クラス(以下、Developerクラス)を示すクラス図だ。

ホワイト・ボードに書かれたクラス図
このクラス図は開発者クラスを表している。

 要件1の「モチベーション指数を取得できる」を実現するために、開発者のモチベーション指数を取得するための次の3つのメソッドを、このDeveloperクラスに実装する。

  • 「どのくらいテストが好きか」
     → HowMuchGreenbarLoverメソッド

  • 「どのくらいリファクタリングを行っているか」
     → HowHeavyRefactorメソッド

  • 「どのくらいソフトウェア開発を楽しんでいるか」
     → HowHappyメソッド

 さらに、要件2の「開発者には種類が存在する」に基づき、次のフィールドをDeveloperクラスに用意する。

  • 「一般的な開発者/アジャイルな開発者」
     → DeveloperTypeインスタンス変数

 開発者の種類には「一般的な開発者/アジャイルな開発者」の2つがあるが、上記のクラス図からはその情報を読み取ることはできない。つまり開発者の種類による振る舞いの変化は、Developerクラス内部に隠ぺいされる。取りあえずここまでは、シンプルで問題がなさそうに思える。

 それでは次に、テスト駆動開発の手順にのっとり、DeveloperクラスのNUnit用テスト・コードを記述してみよう(NUnitについては前掲の記事を参照してほしい)。

 以下は実装したテスト・コードである。

Imports System
Imports NUnit.Framework
Imports DesignPatterns.Core

Namespace DesignPatterns.Tests

  <TestFixture()> Public Class DeveloperTest

    <Test()> Public Sub GeneralDeveloper()
      Dim d As Developer = New Developer(Developer.General)
      Assert.AreEqual(3, d.HowMuchGreenbarLover(), _
        "特にテストが好きではないはず。")
      Assert.AreEqual(3, d.HowHeavyRefactor(), _
        "特にリファクタリングは行っていないはず。")
      Assert.AreEqual(3, d.HowHappy(), _
        "特にソフトウェア開発が楽しいとは思わないはず。")
    End Sub

    <Test()> Public Sub AgileDeveloper()
      Dim d As Developer = New Developer(Developer.Agile)
      Assert.AreEqual(5, d.HowMuchGreenbarLover(), _
        "テストが大好きなはず。")
      Assert.AreEqual(5, d.HowHeavyRefactor(), _
        "リファクタリングは絶えず行っているはず。")
      Assert.AreEqual(5, d.HowHappy(), _
        "ソフトウェア開発が楽しくてしようがないはず。")
    End Sub

  End Class

End Namespace
Developerクラスのテスト・コード(VB.NET)

 このテスト・コードを参照すると、テストされるDeveloperクラスの設計において、次のような意図を読み取ることができる。

  1. Developerクラスのコンストラクタは、開発者の種類を表す値(Developer.GeneralまたはDeveloper.Agile)を受け取る

  2. Developerクラスのインスタンスには、一般的な開発者インスタンスとアジャイルな開発者インスタンスが存在する

  3. 一般的な開発者インスタンスとアジャイルな開発者インスタンスでは、同じメソッド呼び出しの結果として異なるモチベーション指数(「3」や「5」)が返される

 以上の項目を踏まえて、NUnitによるすべての単体テスト(ユニット・テスト)をパスするDeveloperクラスの実装コードを記述すると、以下のようになる。

Imports System

Namespace DesignPatterns.Core

  Public Class Developer

    Public Const General As Integer = 0
    Public Const Agile As Integer = 1

    Private _developerType As Integer

    Public Sub New(ByVal developerType As Integer)
      Me._developerType = developerType
    End Sub

    Public Function HowMuchGreenbarLover() As Integer
      If (_developerType = General) Then
        Return 3
      Else
        Return 5
      End If
    End Function

    Public Function HowHeavyRefactor() As Integer
      If (_developerType = General) Then
        Return 3
      Else
        Return 5
      End If
    End Function

    Public Function HowHappy() As Integer
      If (_developerType = General) Then
        Return 3
      Else
        Return 5
      End If
    End Function

  End Class

End Namespace
単体テストをパスするDeveloperクラスの実装コード(VB.NET)

 Developerクラスには、同じ条件判定を行うif文が複数存在している。これは新しい種類の開発者が追加されるたびにすべてのif文に対して修正を行う必要があることを意味する。すなわちこの実装コードは、修正漏れなどによりバグが入り込む可能性が高いという問題を抱えているのである。このコードからはリファクタリングでいうところの「不吉な匂い」が漂い始めている。単体テストをすべてパスしたからといって、このまま安心していてはいけない。

 このような状況に対してデザインパターンを適用すると、このクラスがどのように改善されていくのかを次にご覧いただこう。

 

 INDEX
  .NETで始めるデザインパターン
  第1回 .NET開発におけるデザインパターンの有用性
  1..NETにおけるデザインパターンの必要性
    2.デザインパターン適用による問題解決のメリット
 
インデックス・ページヘ  「.NETで始めるデザインパターン」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間