アーカイブ | Advent Calendar RSS feed for this section

C#っ! to the future

25 12月

ネタ切れネタ切れって 言いたい事言いやがって ネタなんて最初からねーんだよ! ブロガーが消費してんのは 寿命だけだオラ!

(意訳: ネタ要素薄くてごめんなさい。)

 

最後に、この無謀な25日間の内容から、要点を抜き出しましょう。将来の方向性の示唆にもなると思います。

上昇傾向

初日に、C#の歴史を振り返ってみました。

 

最初は政治的(SUNとの利害関係) 、経済的(DelphiのアーキテクトであるAnders Hejlsbergがマイクロソフトに引き抜かれてる)な理由で始まったC#ですが、3.0、4.0とバージョン アップを重ね、非常に素晴らしい言語に育ちました。まだまだ伸びるでしょう。

こんな話もあります:

1企業の調査なので、まあ、話半分にとらえるべきかもしれませんが、C#はずっと伸び続けています。

昔だったらJavaなどが採用されそうだったような状況で、C#が採用される場面も出てきています。Unity、PlayStation Suiteと、ゲーム向け開発プラットフォームでの採用が続きました。

ちなみに、日本においても、C#関連のホームページのアクセス数や、書籍の売り上げ、伸びてきているようです。

データ処理と非同期

C# 3.0以降の進化の方向性は、データ処理と非同期処理に焦点が当たっています(C# 2.0までは、いわば基礎固めと言った感じで、既存言語と比べてそれほど目新しいことはしていません)。

 

非同期処理は、これからもまだまだ進歩していく分野でしょう。データ処理においても、ネットワーク越しに別のサーバーからデータを取ってくることが多く、非同期I/Oが重要です。

広い範囲の視野持ってほしい

この25日間で、非常に広い範囲の話をしてきました。.NET自身が広い範囲をカバーしているというのもあります。

 

内部に踏み込んだ話もいくつかしました。

 

設計パターンみたいな話もしました。

 

そして、プログラミングはあくまでも手段であって目的ではありません。広く使える手段となる.NETでは、広い目的を果たせます。5日目には、統計の話なんかもしました。

 

どれも重要なことだと思って、25個の話を書きました。

覚えることが多くて大変と思うかもしれないですが、今、.NETに限らず、IT技術者に求められている必要な知識だと思います。

今は、分業化も進んでいて、各個人が直接必要となる範囲は狭いかもしれませんが、協業相手との円滑なコミュニケーションのためにも、概要くらいは広く知る心構えを持ってください。

共通ライブラリ

何回かは、.NETのライブラリの紹介などもしました。

ほんの一角しか紹介できませんでしたが、.NETには様々なライブラリがあります。

18日目に話しましたが、今は、言語を覚えることよりも、ライブラリを覚えることの方が重要、かつ、大変です。

1つのライブラリを、いろんな言語から利用できるというのは非常に大事です。これは、.NETだけでなく、他の環境でも言えることでしょう。ネイティブと.NETでの共通利用、静的言語(C#やVBなど)と動的言語(IronPythonなど)との間の相互運用も重要です。

銀の弾丸はない

「AかBかどっちがいい?」みたいな質問の答えは、だいたい、「両方」もしくは「中間」になります。

 

GUIかCUIかと言われたら、両方。

静的か動的かと言われたら、両方。

ネイティブかマネージかと言われたら、両方。

ウェブかクライアントかと言われたら、両方。

OOPか関数型かと言われたら、両方。

 

それが現実です。

重要なのは、AとBそれぞれ、適切な利用場面がどこか、そして、選べるかどうかです。

ツール連携

コンパイル時と実行時では、コンパイル時にエラーが発覚してくれた方が、エラーを発見しやすいです。Visual Studioに至っては、コンパイル時どころか、タイピングしたそばからコードが解析されて、リアルタイムにエラー内容が見えます。それが静的な言語のよさでもあります。

一方、動的言語と呼ばれるようなタイプのプログラミング言語は、スクリプティングやメタプログラミングしやすいという利点があります。

ここで再び、「AかBか?」と聞かれたら、両方! C#も、動的な特性や、スクリプティング、メタプログラミングの方向に進歩しようとしています。

 

しかしそれでも、基本方針は、可能な限り静的(コンパイル時)処理、そして、Visual Studioとの連携ありきです。先だって、Roslynで、Visual Studioとの連携を深めようとしています。

 

スクリプト(文字ベースの操作)であっても、GUIの補助により生産性を高めることができます。

 

また、C#という言語に新しい機能を入れるにあたっては、ライブラリやVisual Studioまで含めた視点で、慎重に考える必要があります。

 

クロス ターゲットな”コア”

「AもBも」といろいろ求められる昨今、クロス ターゲット、run anywhereに作れる部分と、そうでない部分をきっちりと分離しましょう。

 

「これはクロス ターゲットに利用可能だろう」と思っていたものが、実はそうでなかったりもするので少し注意が必要です。

10年も前だと、ファイル読み書き(.NETでいうとSystem.IO.Fileクラス)が非ポータブルだなんて思いもしなかったものです。今だと、ファイル読み書きはセキュリティ的なお荷物です。

開発プロセス

Visual Studioは、個人のコーディング → 個人のテスト → 開発チームの連携 → 統制・開発・運用含めた連携 と進歩してきています。

 

「ツールに使われてるみたいで嫌」と思うかもしれませんが、ツールの補助は、ある意味教科書みたいなものです。素直に従ってればいいというものでもありませんが、少なくとも、なぜツールがそうなっているかは考えてみた方がいいでしょう。

色々余計な負担になることもあります。しかし、必要なことを色々端折って始めるのは簡単ですが、後からつらくなることが多々あります。いわば、負債と引き換えに歩を進めているようなものです。リリース後の継続運用・継続開発が重要になっている昨今、負債をしょい続けていいものかどうか、良く考える必要があります。

広告

.NET関連技術

25 12月

C#たんは最初から最後までクライマックスだぜ!

 

この記事は、C# Advent Calendarの方の25日目です! 私の1人Advent Calendarの方は、そっちはそっちで書きます!

今日は、C#や.NETと、その周辺のキーワードを網羅的に紹介していこうかと思います。

.NET周辺キーワード

軽く図にしてみました。

 

コア

クロス ターゲットに使える部分です。.NET Frameworkの標準ライブラリの中でも、基礎中の基礎。

  • Text/RegularExpressions: テキスト処理と正規表現
  • Collections: コレクション
  • LINQ: データ列の加工や集計(.NET 3.0以降)
  • Math: 数学関数
  • Numerics: BigInteger(任意精度整数)とComplex(複素数)(.NET 4以降)
  • IO: ファイルなどの入出力
  • Net: ネットワーク アクセス
  • Reflection: 実行時型情報
  • Expressions: 式ツリー(ラムダ式をデータ化)、動的コード生成
  • Task: 同時実行(並列処理、非同期処理)
  • Service Model: サービス化(システムの備品化、疎結合化)
  • MEF (Managed Extensibility Framework): プラグイン拡張

 

クライアント

いわゆるビュー、プレゼンテーション層技術、GUIフレームワークです。XNA以外は、同じスタイルで開発(XAML+C#、Visual StudioやBlendによるRAD開発)できます。ただし、GUIのクロス プラットフォーム開発は割かし幻想で、ターゲットごとに微妙に差があります。

  • WPF: デスクトップ向け
  • Silverlight(ブラウザー): ブラウザー プラグインとして提供。FireFoxや、Safari上でも動きます
    • デスクトップ向けの.NETと比べると、使えるライブラリが少ないです
    • コアの部分は使えます
  • Silverlight(Windows Phone 7): Windows Phone 7向け。
  • Metro/WinRT: Windows 8なタブレット端末向け(現在、早期プレビュー版)
  • XNA: アクション性の高いゲーム向けのフレームワーク
    • イベント駆動型ではなく、メッセージ ループを直接書く
    • Windows PC、Xbox 360、Windows Phone 7上で動きます

 

加えて、ASP.NETでは、HTMLをサーバー上で生成して、クライアント側はブラウザーを使って表示というパターンもあります。HTML5にも対応しています。最近はjQueryに投資しているようです。

通信

WCFは、.NETにおけるサービス化の要です。ネットワークをまたいで、インターフェイスのメソッドを呼び出すようなプロキシの役割を担います。

元々は、いろんなタイプの通信プロトコルを統一的に扱うためのものでしたが、最近はHTTPを使ったウェブ サービスに最も注力しています。

昔は、SOAPというメッセージ交換プロトコルを主軸にしていましたが、今は、REST形式のプロトコルであるODataを主軸としています。XMLだけでなく、JSONにも対応しています。

アプリ サーバー

処理の大部分をサーバー上で行うようなアプリを開発するためのフレームワークとして、ASP.NETがあります。

サーバー上でHTMLを生成してブラウザーで表示する場合もあれば、ウェブ サービス化しておいてクライアント アプリから呼び出す場合もあります。

  • ASP.NET Web Forms: 通信層を隠ぺいして、デスクトップ アプリと同じ感覚でウェブ アプリを作れます
    • 内部を隠ぺいしすぎて不評というのもあったりします
  • ASP.NET MVC: .NET以外の言語のウェブ フレームワークに多い、MVC構造を採用したフレームワーク
    • HTTPを下手に隠ぺいするのはやめて、規約ベースでURLと処理の対応付けします

 

特に、ASP.NET MVCでは、HTMLの生成用のテンプレート部分に、aspx形式と、Razor形式(拡張子cshtmlかvbhtml)を選べます。Razorでは、<%%> のような不恰好なコード ブロックではなく、@ を使って簡潔にコードを書けます。

データ アクセス

C#などの.NET言語からデータベースにアクセスするためのフレームワークとして、ADO.NETというものがあります。

特に、.NET 4から標準に入った、ADO.NET Entity Frameworkというものを使うと、ER図的なデータ設計モデルからデータベースや、エンティティ クラス(.NET側からデータベースにアクセスするために使うクラス)を作ったりすることもできます。

  • Database-First: データベースからエンティティ クラスを生成します
  • Visual-Model-First: Visual Studio上で、ER図(Entity Relationship Diagram)的なGUIを使ってエンティティを設計し、データベースやエンティティ クラスを生成します
  • Code-First: 素のクラス(何も継承しないし、内部に何も特別な処理を書かない、純粋にデータを表すクラス)から、データベースやエンティティ クラスを生成します

サーバー インフラ

  • WF (Windows Workflow Foundation): フローチャート的な処理手順を、GUI上で編集して実行できます
    • SharePointサーバーやTFSのカスタマイズに使えます
    • 次期バージョンでは、バッチ処理をWFで書いて、PowerShellから起動というようなこともできるようになります
  • WIF (Windows Identity Foundation): 認証基盤

サーバー製品

サーバー アプリを作るなら、それを動かすためのサーバー製品が必要になります。オン プレミス(On Premise: 自前でハードウェアを持って自前で管理する)版と、クラウド(PaaS、管理もお任せ)版の両方が提供されています。

  • Windows Server/Window Azure: OS
  • SQL Server/SQL Azure: データベース サーバー
  • AppFabric: サーバー間の連携や、アクセス制御を担うインフラ

サーバー管理は、RDP(リモート デスクトップ接続)かPowerShellを使います。繰り返しになりますが、GUIにもCUIにもそれぞれいいところがあって、両方需要があります。Azure(のVM Role)ではクラウド相手ですらRDP管理できます。

また、Office製品と連携する業務基盤であるSharePointというサーバー製品もあります。OfficeやSharePointのカスタマイズも、Visual Studioと.NETを使ってできます。

開発インフラ

.NETと言えば、強力な統合開発環境。

  • Visual Studio: 説明不要かと思います。.NETの生産性の高さの原動力
  • TFS (Team Foundation Server): バージョン管理、作業項目管理、継続的インテグレーションなどを行うためのサーバー製品
  • Team Foundation Service: TFSのSaaS版。Azure上で稼働(現在、プレビュー版

Visual Studioは、2010から、拡張が簡単になりました。拡張マネージャーというものを使って、サード パーティ製のVisual Studio拡張を簡単にインストール可能です。拡張を作る側は、Visual Studio SDKというものを使います。

Roslynで一番期待されているのも、Visual Studio拡張によるメタプログラミングです。

現状だと、ビルド時コード生成によるメタプログラミングでは、T4テンプレートを使います。

コミュニティ

ライブラリ

現在では、約2年周期の大きなアップデート(Vistaから7にとか、Visual Studio 2008から2010にとか)の合間合間にも、オープン ソースで広くフィードバックを厚めながら新機能を提供しています。

コードの公開にはCodePlexというオープン ソース コミュニティ向けサイトを利用しています。

いくつか例を挙げましょう。

マイクロソフト公式のもの以外にも、コミュニティ ベースのToolkitもあります。

その他、CodePlexには有用なオープン ソース ライブラリがたくさんあるので、サイト内をいろいろ検索してみてください。

こうして作られたライブラリ(マイクロソフト公式の追加ライブラリや、コミュニティ ベースのもの)は、最近だと、NuGetという、オンライン ギャラリー + Visual Studio拡張を通して、簡単にプロジェクトから参照できます。

情報提供

マイクロソフトの開発者向け製品に関する情報は、MSDN(Microsoft Developer Network)というサイトに集約されています。

  • ライブラリ: APIリファレンスなど、学習コンテンツ
    • 初心者が学ぶには詳しすぎてしんどいなんて話もありますが、網羅性と詳細さは非常に良いです
    • 日本からは、「英語の情報があるってことは、まだ日本語未対応なんでしょ。そんな情報要らない」というフィードバックと「英語でもいいから最新の情報もらえないと困る」というフィードバックが半々で、途方に暮れているそうです
    • 「機械翻訳でもいいから早く」と「機械翻訳とか役に立たねー」も半々
  • ブログ: 社員によるブログ
    • 最新の情報はだいたいここ(の本社社員の記事、もちろん英語)から出てきます
  • マガジン: 毎月刊行される情報サイト
    • 元々は本当に紙の冊子も販売されてた
  • フォーラム: フォーラム(掲示板)形式で、質問や意見/要望を受け付けています
  • Code Recipe: コード付きのサンプル/解説置き場
    • 元々は公式コンテンツしかなかったですが、今ではユーザー投稿(誰でも可能)を受け付けています

多様化

C#やVBだけが.NETじゃなく、マイクロソフトだけが.NET提供しているわけではなく、PCだけが.NETの対象ではありません。

  • DLR (Dynamic Language Runtime): .NET上に動的言語を実装するための基盤
    • マイクロソフトの寄与度が高いものでいうと、IronPythonが提供されています
    • その他、IronRubyなど、いくつかの動的言語がDLR化されています
    • DLR上に実装された動的言語からは.NETのライブラリを利用でしますし、動的言語側で作ったクラスなども、C# 4.0のdynamicキーワードなどを使って相互運用可能です
  • mono: オープン ソースな.NET実装です
    • Mac向けやLinux向け、iOS向けやAndroid向けなども提供されています
    • UnityPlayStation Suiteなどのゲーム開発フレームワークでも採用されています
  • Robotics Studio: C#などを使ってロボットを制御
  • .NET Micro Framework:組み込み機器向け.NET
    • OSすら載せたくないような低フットプリント機器向けに、.NETの最低限の機能だけを提供
    • オープン ソース提供されています
    • Porting Kitを使って特定のハードウェア向けにカスタマイズすることもできます
    • 元から対応しているハードウェア基盤の購入もできます
    • モジュールの組み合わせでプロトタイプ作成ができる、.NET Gadgeteerというプロジェクトも

メタプログラミング

24 12月

C++たんがまた今年も一人パーティ用のケーキとシャンパン買ってましたが、皆様いかがお過ごしでしょうか。

 

さて、本日はメタプログラミングがテーマです。

パターン

プログラミングには、「こういう場面はこう書くべきです」というようなパターンがつきものです。

この手のパターンは、昨日も少し話したような一種の「失敗しないための教科書」です。守らないと、アプリの欠損を招く確率を跳ね上げます。それはもう、たいそう跳ね上げます。

ある種のパターンは、ライブラリ化することで回避できたりします。しかし、ライブラリにできないパターンというのもあって、そういうものは、ちゃんとパターンを覚えてコードを書かないといけません。

ということで、言語構文を一通り覚えたら、次は、パターン本を読んで全部覚えろと言われるわけです。

しかし、それを社内すべてのエンジニアに期待できるのかというと、なかなか難しいのが実情です。そして、悪貨は良貨を駆逐する。しっかり勉強してる人が、わかってない人のコードに疲弊して、ばかばかしくなって辞めていくわけです(※末尾に補足あり)。

パターンを言語機能化

ライブラリ化しにくいパターンも、言語機能として何らかのサポートを加えることで回避できることがあります。

「このパターン通りに書け」だと意識の高い人しか実践してくれませんが、「この文法使うと便利だよ」なら割とみんな使ってくれます。

C#にはそんな、パターンだったものを文法化したものがいくつかあります。

イベント

いわゆるイベント駆動型のプログラムを作る場合、オブザーバー パターンというものを使います。C#のイベント構文を使わずに書くと、以下のようなパターンになります。

object _progressSync = new object();
Action<int> _progress;

// イベント ハンドラーの登録
public void AddProgressHandler(Action<int> handler)
{
    lock (_progressSync)
        _progress += handler;
}

// イベント ハンドラーの解除
public void RemoveProgressHandler(Action<int> handler)
{
    lock (_progressSync)
        _progress -= handler;
}

デリゲート(マルチキャスト可能な関数オブジェクト)の概念を持っていない言語だと、もうちょっとめんどくさいパターンになります。

C#の場合、オブザーバー パターンに相当する機能は、イベント構文という専用の構文があって、1行で書けます。

public event EventHandler<int> Progress;

これで、上記のコードと同じ挙動が得られます(具体的な実装はちょっと違いますが)。

イテレーター

データ処理、例えば、データ列の中から特定の条件を満たす要素だけを取り出すというような処理を考えてみましょう。一般に、この手の処理にはイテレーター パターンというものを使います。これも、C#の専用の言語機能に頼らず書くと、以下のようになります。

class WhereEnumerator<T> : IEnumerator<T>, IEnumerable<T>
{
    IEnumerator<T> _e;
    Func<T, bool> _cond;
    public WhereEnumerator(IEnumerator<T> e, Func<T, bool> cond)
    {
        _e = e;
        _cond = cond;
    }

    public T Current { get { return _e.Current; } }

    public bool MoveNext()
    {
        while (_e.MoveNext())
            if (_cond(_e.Current))
                return true;
        return false;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return this;
    }
// 
一部省略
}

public static IEnumerable<T> Where<T>(IEnumerable<T> source, Func<T, bool> cond)
{
    return new WhereEnumerator<T>(source.GetEnumerator(), cond);
}

C#にはイベント構文というものがあって、このパターンを劇的に簡素化できます。以下の通りです。

public static IEnumerable<T> Where<T>(IEnumerable<T> source, Func<T, bool> cond)
{
    foreach (var x in source)
        if (cond(x))
            yield return x;
}

 

それでもまだまだパターン

そんなC#でも、まだまだパターンを書かないといけないことは多いです。

あるオブジェクトのあるプロパティの値が変化したことを、他のオブジェクトに伝えたいことがあります。データベース エンティティ変更追跡とか、GUIへの反映なんかが主だった例です。

この場合、結構面倒なコードを書く必要があります。例えば、XとYという2つのプロパティを持つだけの単純なクラスすら、以下のようになります。

class Point : INotifyPropertyChanged
{
    private int _x;
    public int X
    {
        get { return _x; }
        set { _x = value; RaisePropertyChanged(“X”); }
    }

    private int _y;
    public int Y
    {
        get { return _y; }
        set { _y = value; RaisePropertyChanged(“Y”); }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string name)
    {
        var d = PropertyChanged;
        if (d != null)
            d(thisnew PropertyChangedEventArgs(name));
    }
}

 

メタプログラミング

こういう時、例えば、以下のような簡素なクラスから、先ほどのようなパターンで煩雑化したクラスを自動生成したかったりします。

class PointDefinition
{
    int X;
    int Y;
}

 

こういう発想を、プログラムからプログラムを作るという意味で、メタプログラミング(meta-programming)と呼びます。

こういうのは、動的言語と呼ばれるようなタイプのプログラミング言語が得意とするところです。

まあ、C#でも、少し煩雑ですが、動的コード生成で対処できないことはないです。が、以下のような理由で敬遠されます。

  • なんだかんだ言って結構煩雑
  • この手の処理なら、コンパイル時コード生成でもできるんだから、動的(実行時)に生成したくない
    • コンパイル時にエラーがわからない(実行して初めてわかる)のが嫌
    • 性能を落とすのが嫌

 

なので、「C#にも、C言語の#defineみたいな置換マクロが欲しい」みたいな意見も時々聞きます。ただ、C#の場合には、Visual Studioとの連携も必要なので、マクロ(単純な文字列置換)では難しいです。もし導入しても、Visual Studioの補助が受けれなくなるデメリットの方が大きいです。

T4テンプレート

C#でのメタプログラミングは、本格的にやるならRoslynを待った方がいいかもしれません。

まあ、今現在の次善の策としては、T4テンプレート(Text Template Transformation Toolkit)というものを使うのがいいでしょう。コンパイル時のコード生成用のツールで、Visual Studio 2010からは標準で入っています。

結構慣れるまでが大変、かつ、そんなに使いやすい文法でもないんですが、以下のような感じです。

using System.ComponentModel;

public class <#= Def.Class #> : INotifyPropertyChanged
{
<#
foreach (var p in Def.Properties)
{
#>
    private <#= p.Type #> _<#= p.Name #>;
    public <#= p.Type #> <#= p.Name #>
    {
        get { return _<#= p.Name #>; }
        set { _<#= p.Name #> = value; RaisePropertyChanged(“<#= p.Name #>”); }
    }

<#
}
#>
    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string name)
    {
        var d = PropertyChanged;
        if (d != null)
            d(this, new PropertyChangedEventArgs(name));
    }
}

これで、簡素な定義クラスから、煩雑なパターン生成できます。上記のテンプレートをNotifyPropertyChanged.ttincというファイル名で作ったとして、それとは別に、以下のようなテンプレートを作ります。

<#@ template debug=”false” hostspecific=”false” language=”C#” #>
<#@ output extension=”.cs” #>
<#
var Def = new
{
    Class = “Point”,
    Properties = new[]
    {
        new { Type = “int”, Name = “X” },
        new { Type = “int”, Name = “Y” },
    },
};
#>
<#@include file=”NotifyPropertyChanged.ttinc” #>

その結果、先ほど作ったようなPointクラスが自動生成されます。

メタプログラミングする上で

メタプログラミングするに当たっては、難儀な問題がいろいろあったりします。いくつか説明していきましょう。

覚えるコスト

メタプログラミングは、既存の言語の文法の中に、別の文法を作るようなものです。新しい言語を覚えるコストが余計にかかります。

ツールとの連携

「パターンを覚えるにしても、新しい文法を覚えるにしても、(あるいはライブラリでやるなら)新しいクラスやメソッドを覚えるにしても、覚える量には変わりがないじゃないか」と思うかもしれません。

一般にはその通りなんですが、C#の場合、Visual Studioという強力な補助ツールがあるわけです。クラスやメソッドの追加の場合、Visual Studioの補完を使えます。しかし、新しい文法を考える場合、Visual Studioまで対応させるのは結構難しいです。

先ほどのT4テンプレートが微妙なのも、Visual Studioの補助がいまいちだからというのが大きいです。

読みやすさと書きやすさ

プログラミングという作業では、書きやすさよりも、読みやすさの方が大事です。書いた本人とは違う人がコードを読む機会が多いです。また、自分の書いたコードであっても、ちょっと前のことはすぐに忘れます。

メタプログラミングも、後から読んで困るほどの省略はしてはいけません。

まあ、煩雑なパターン コードよりは読みやすいはずなので、変な略語とかだけ使わなければ大丈夫でしょう。

なんでもはやろうとしない

メタプログラミングは、汎用プログラミング言語の構文に漏れるような、ある特定の領域に対して特化した構文を作るのに使います。

「特定の領域に絞る」というのは非常に重要です。メタプログラミングで何でもはやろうとしないでください。それは、汎用言語構文の役割です。

補足

 

悪貨に駆逐されないために

本題と外れるのでいったんスルーしましたが。

「ダメな人のコードにできる人が疲弊する」というので、本当にまずいのは、組織的な問題の部分です。組織運用の在り方から考えなおす必要があります。

「ダメの人のコード」というように、個人に帰着した問題になっている時点でアウトです。コード レビューなどを通してダメなコードをはじく仕組みを、チーム全体でやっていく必要があります。コードの責任を個人のものにしてはいけません。その責任はチームで持つべきです。

個人からチームへ

23 12月

今日のテーマは共同開発です。

 

非常に大事というか、会社に入ると当たり前な話なわけですが。あんまり学校の授業では出てこないみたいで、学生さんには欠けがちな視点みたいですねぇ。

視野の広がり

Visual Studioというのはソフトウェア開発の生産性を上げるためのツールです。ソフトウェア開発と言っても、いろんな視点があって、いろんな機能提供があります。以下の絵は、Visual Studioの新機能追加の歴史のようなものです。

この視点の変化(というか、視野の広がり)は、Visual Studioに限らず、ソフトウェア開発の現場一般に言えることでしょう。視野は、個人からチームへ、開発チームからビジネス全体へと広がっています。

.NET以外の世界でいうと、

  • エディター(Eclipseなど)
  • ソースコード管理(SubversionやGit)
  • 進行管理(TracやRedmine)
  • 継続的インテグレーション(Jenkinsなど)

など、多くのツールを組み合わせて実現します。

Express版

昨日のテストの話でもしましたね、初心者だと無償のExpress版で十分だと。それは、初心者だとだいたい、個人視点に収まるからです。前節の図で言うと、「開発者」に位置する機能は、Express版でもかなり網羅しています(単体テスト機能はないですが)。

一方、図中の右に行くほど、Visual Studioの上位エディションにしかない機能が増えます。

アプリのライフサイクル管理

開発チームという視点から、ビジネス全体へと視野が広がっているのには、時代の変化という背景もあります。

  • パッケージ売りから継続運用への変化
    • 昔は、一度リリースしてしまったらもうおしまい(あるいは、バージョン アップまで結構間が空く)でしたが、今は、ウェブなどで、継続的に要望が入り、継続的に開発を行う必要があります
  • アジャイル開発
    • 最初に要望を聞いて終わりではなく、細かい単位で要望や優先度の見直しが行われるようになっています
    • そもそも、世情の変化も早く、要望自体が短期間で変化します

開発チームだけで閉じていては、世情の変化に迅速に対応することができず、ビジネスの機会を失います。

アプリは、開発だけがすべてではありません。開発の前段階の要求の洗い出し、リリース後の運用、さらに、フィードバックを受けての次段階の検討まで含めたライフサイクルの管理が必要です。この考え方に対して、ここ数年で、ALM(Application Lifecycle Management)という呼び名も生まれました。

ビジネスにかかわる関係各者

開発チームからビジネス全体へという話をもう少し掘り下げてみましょう。

プロジェクトが大きくなればなるほど分業化が進むわけですが、以下のようなポジションに分かれることになります。

  • 統制
    • 意思決定者です
    • 何を作る、何を優先する、何にどれだけのお金を投資するなどの最終判断を下します
    • 受託開発の場合だと、顧客との折衝なども含まれます
  • 開発
    • アプリ開発も、いろいろな人員の協業で成り立っています
    • (狭義に)開発
    • 実際にアプリを設計し、コーディングします
    • 進行管理
      • チーム内の開発状況に遅れがないか、あるなら解決方法は何かなど、チームの進行状況の管理や他チーム/統制との折衝を行います
    • テスト
      • 開発者本人のテスト記述では漏れるような第3者視点でのテスト、結合テスト、手動テストなどを行います
    • UX(ユーザー体験)デザイン
      • 人文科学的視点や、美術的な視点からアプリの操作性や外観を設計します
  • 運用
    • アプリ公開後、継続的にサーバーの管理や不具合報告、エンド ユーザーからのフィードバック受け付けなどを行います

 

個別の視点、個々人の戦術的な視点だけではプロジェクトは成功しません。全体を戦略的に眺める視点が必要です。

広い範囲全体を眺めるというのは、言うほどたやすいことではありません。ソフトウェア開発に対する要求が高度化するにつれ、この全体の把握を補助するツールの存在が重要になっています。

TFS

チーム全体の共同作業を支援するため、(クライアント アプリである)Visual Studioの他に、サーバー製品も提供されています。それが、TFS(Team Foundation Server)です。

Visual Studioとの連携(Visual Studio上からのソースコードや作業項目管理)に加えて、SharePointベースの管理ポータル ウェブ サイトを持っています。

今後は、Azure上に構築され、オン デマンドに使えるクラウド版TFS、Team Foundation Serviceも提供される予定です(現在、招待性のベータ版)。

Visual Studio 2010/TFSの機能

現状のVisual Studioが持っている機能を簡単に紹介しておきましょう。「ビジネス全体」の視点の機能は現在も拡充途中で、次期バージョンでもいろいろと新機能が入る予定です。

コーディング

  • コードの補完
  • リアルタイムなビルド/エラー検知
  • コードの構文ハイライト
  • 検索
  • リファクタリング
    • メソッド化、フィールドからプロパティの生成、変数/メンバー名変更
  • メンバー生成
    • 先にメソッドなどを使う側のコードを書いて、そこから未実装のメンバーを作る

デバッグ

  • ステップ実行
    • 1行ずつ実行しながら、変数の状態などの変化を見る
  • ブレイク ポイント
    • 特定の行を通った時に1度実行を止める
  • デバッグ情報の履歴保持(IntelliTrace)
    • デバッグ実行時の状態変化の履歴を、チーム内の別の誰かにそのまま渡して再現する機能

コード分析

  • メトリック(metric: 測定基準、数値化した指標)計算
    • バグを起こしがちなソースコードを判定するための指標を出す
  • プロファイリング
    • 性能分析

テスト

  • 単体テストの生成
  • テストの選択的実行
    • 現在カーソルのあるメソッドに関連する部分だけとか、修正した部分だけとか
  • テストの自動実行
    • TFS上で、ソースコードのコミット直後や、所定の時間にテストを実行
    • テストに通っていないコードをソースコード管理サーバーに反映させないということも可能
  • 多環境テスト
    • 仮想マシン上にいろいろな環境を構築して、その上でのテスト実行
  • 手動テストの作業項目管理/バグ レポート
  • UIの自動テスト
  • 負荷テスト

進捗管理

  • 作業項目管理
  • 状況の可視化
    • 作業項目の消化度合い
    • 残りのバグ数
    • 各種メトリック

補足

 

失敗の教科書

教科書的なプロジェクト管理/評価指標や、ツールによるその補助を軽視していませんか?

「これをやれば成功する」なんてものがないのは明白です。そんなのがあればみんなやります。しかし、「ここを外したら失敗する」みたいなものは、結構あります。教科書は、「成功するためのもの」ではなくても、「失敗しないためのもの」ではありえます。それをおろそかにして、失敗していませんか?

Visual Studioは、Premium版以上を買うと、様々なメトリックを計算して出してくれます。これもその「失敗しないためのもの」の一種です。数字を良くすればプロジェクトが成功するというものではないですが、数字が悪いほどプロジェクト失敗の確率が上がります。

統計的な研究あり

各種メトリックと、プロジェクトの欠陥に関する統計的な研究も、Microsoft Researchから出ていたりします。

ここでは、プロジェクトの欠陥の指標として、「リリース後に発見されたバグの数」と各種メトリックの適合率を求めているようです。Visual Studioが計算してくれるようなコードがらみのメトリックとは、大まかに、70~80%くらいの適合率が出ているようです。

そして、実は、組織的なメトリック(何社かかわっているかとか、チーム内のエンジニアの人数とか、ちょっとだけかかわって辞めていったエンジニアの人数とか)の方が重要で、コードがらみのメトリックよりもプロジェクト欠陥との適合率が高い(85%程度)という報告もあります。

また、地理的な距離よりも、組織的な距離の方が如実に欠陥に現れるそうです。何kmも離れた場所にいる同社同僚の方が、すぐ隣の他社出向社員よりも、心理的には「近い」とか。

マイクロソフト内の開発プロセス

マイクロソフトは、社内の開発プロセスがどういう風になっているかもオープンにしていたりします。

 

マイクロソフトでは、例えばVisual Studioという製品だけを取ってみても3500人以上の人間が開発にかかわっています。これだけの人数を一括して管理できるわけはなくて、以下のような分散管理になっています。

  • 10人程度のチームに分かれています
    • 進行管理1名、開発5名以下、テスト5名以下
  • 開発手法はそれぞれのチームが自由にやっていい。ただし、
    • 報告を義務付け
    • コミット前にクオリティ チェックがある

そして、分散した開発状況全体を把握するために、TFSの可視化機能が使われています。また、開発とテストの比率がほぼ1対1なことも注目に値するでしょう。

単体テスト

22 12月

プログラミング始めたての初心者、特に学生さんが相手だったりする場合、いつもこう言っています:

Visual Studio、まずは無料版のExpressでいいですよ。商用利用も可能です。C#に関していうと、コンパイラーの性能制限みたいなのもないです。

 

さて、じゃあ、こんな疑問も浮かぶかと思います:

なんで有償版があるの?それでなんでみんなProfessionalを買うの?

 

その答えの1つが、今日お話しする単体テストです。ということで、今日のお話は、Express版では試せなかったり…。逆に、普段からProfessionalよりも上のグレードを使いこなしてる人には少し退屈かも。

下準備: 今回のテスト対象

今日はベタに、簡単な計算をするメソッドを作って、それの単体テストをしてみましょう。

とりあえず、物自体(つまり、テストの対象)を作ります。UIも何もなし、ライブラリだけ作りましょう。名前は、取り合えず、適当にMyMathとかつけておきます。

 

プロジェクトを作ると、Class1.csというファイルができていると思います。まず、こいつをCalculator.csにリネームしてください。

ソリューション エクスプローラーでリネームすると、ファイル名だけじゃなく、中のクラスもClass1からCalculatorに変わるはずです。

まあ、簡単な例ということで、ただ単に足し算するだけのAddメソッドでも作ってみましょうか。Calculatorクラス内に、以下のようなコードを追加してください。

public static int Add(int x, int y)
{
    return x + y;
}

 

テスト プロジェクト/テスト メソッドを作ってみよう

そして、このAddメソッドを右クリック。コンテキスト メニュー内に、「単体テストの作成」というのがあるので、これをクリックしてください。

以下のようなダイアログ画面が出てくるので、OKを押しましょう。

初回は、「テスト用のプロジェクトを作りますか?」みたいなメッセージが出てくるはずなので、適当な名前を付けて「はい」を押してください。今回は、手抜きもいいところですが、デフォルトでつく名前、TestProject1のままで行きます。

 

以下のようなテスト用メソッドができてるはずです。

[TestMethod()]
public void AddTest()
{
    int x = 0; // TODO: 
適切な値に初期化してください
    int y = 0; // TODO: 
適切な値に初期化してください
    int expected = 0; // TODO: 
適切な値に初期化してください
    int actual;
    actual = Calculator.Add(x, y);
    Assert.AreEqual(expected, actual);
    Assert.Inconclusive(
このテストメソッドの正確性を確認します。);
}

テストになるように、中身を書き換えます。まあ、例えば以下のような感じ。

[TestMethod()]
public void AddTest()
{
    Assert.AreEqual(3, Calculator.Add(1, 2));
// 
本当のテストでは、もっとバリエーション持たせましょうね
}

テストで満たすべき条件を、Assertクラスの各種メソッドを使って書いていきます。この例の場合は、AreEqual、つまり、第1引数と第2引数の値が一致していないといけないという条件です。

テストを走らせてみよう

まあ、この例程度ならテストが通って当然ではありますが… なんにしても、本当にテストが通るか、確認してみましょう。

メニューから[テスト]→[実行]→[ソリューションのすべてのテスト]を選ぶか、Cntrl+R, Aとショートカット キーを押してください。

今回は単純なテストが1個だけなので、一瞬でテストが終わると思います。「テスト結果」ウィンドウが現れます。

成功です!

こまめにテストを走らせると、安心してコードを書き換えられます。

Test First

さて、こんな話もあります: テストを先に書け。

「テストがない」という状況を根絶するための工夫です。

実践してみましょう。まず、テスト クラス内(先ほどのAddTestメソッドの下にでも)に、以下のようなメソッドを追加してください。

[TestMethod()]
public void GcdTest()
{
    var testData = new[]
    {
        new { X = 100, Y = 15, Z = 5 },
        new { X = 144, Y = 81, Z = 9 },
        new { X = 13*11*4, Y = 13*9*5, Z = 13 },
        new { X = 1024, Y = 999*2, Z = 2 },
        new { X = 13*11, Y = 9*8, Z = 1 },
        new { X = 360, Y = 80, Z = 40 },
        new { X = 10, Y = 10, Z = 10 },
    };

    foreach (var item in testData)
    {
        Assert.AreEqual(item.Z, Calculator.Gcd(item.X, item.Y));
        Assert.AreEqual(item.Z, Calculator.Gcd(item.Y, item.X));
    }
}

最大公約数(greatest common divisor、略してGCD)を求める想定で、先にテストだけ書きました。この時点では、まだGcdメソッドも作っていないです。

メソッドがないので当然エラーになるわけですが、Visual Studioを使っていると、エラー個所に以下のようなスマート タグが現れるはずです。

この「メソッド スタブを生成します」を選んでみましょう。Calculatorクラス内に、以下のようなメソッドが自動生成されます。

public static object Gcd(int p, int p_2)
{
    throw new NotImplementedException();
}

じゃあ、実装しましょうか、最大公約数。以下の通り。戻り値の型も、objectからintに変えておきましょう。

public static int Gcd(int x, int y)
{
    while (true)
    {
        var r = x % y;
        if (r == 0) return y;
        x = y;
        y = r;
    }
}

再びテストを実行して、成功を確認しましょう。

Expressの場合: せめてテスト用のコンソール アプリを

この、Visual Studio組み込みの単体テストのいいところは、

  • どの範囲のテストを実行するか選んだりもできる
    • 今、エディター上でカーソルのあるメソッドのテストだけ実行するとか
    • 前回失敗したところだけ実行とかも
  • チーム開発機能と組み合わせて、テストに通ったソース コードしか、管理サーバーにコミット(commit: 約束する、制約する。この場合、「ちゃんとできたから責任もってサーバーに反映するね」という意味)できないようにできる

等々あります。

 

まあ、そこまでやらずとも、とりあえずテスト コードを書くだけなら、適当にコンソール アプリでも作って、そこにテストを書けば大丈夫です。この方法なら、Visual C# Expressでもテストが書けます。

NUnit

ちなみに、NUnitという、オープンソースな単体テスト フレームワークもあって、こいつはExpress版にも対応しているそうなので、ぜひとも導入を検討してみてください。

privateメンバーのテスト

さて、privateなメンバーのテストはどうしましょう?プログラミング言語によっては、「privateだとテストできなくなるし、全部publicで作ればいいんじゃない?」なんていう人もいるみたいです。

が、Visual Studioではそんな心配不要です。Visual Studioが、リフレクションを使ってprivateメンバーに無理やりアクセスするAccessorクラスというのを作ってくれます。

例えば、Calculatorクラスに以下のようなメソッドを追加してみましょう。

private static int PrivateMul(int x, int y)
{
    return x * y;
}

こいつを、右クリックして「単体テストの作成」すると、以下のようなテスト メソッド(と、Accessor)が作られます。

[TestMethod()]
[DeploymentItem(“MyMath.dll”)]
public void PrivateMulTest()
{
    int x = 0; // TODO: 
適切な値に初期化してください
    int y = 0; // TODO: 
適切な値に初期化してください
    int expected = 0; // TODO: 
適切な値に初期化してください
    int actual;
    actual = Calculator_Accessor.PrivateMul(x, y);
    Assert.AreEqual(expected, actual);
    Assert.Inconclusive(
このテストメソッドの正確性を確認します。);
}

Calculator_Accessorというクラスが自動生成されていて、こいつは、Calculatorのprivateメンバーを、リフレクションを使って無理やりpublicにアクセスできるようにしたものです。

便利ツール

最後に、単体テストで使える便利ツールを紹介しておきましょう。

Pex

メソッドの中身に応じてテスト コードを生成してくれるVisual Studioアドインです。テスト漏れが起きないように、きわどいケースなんかを生成してくれます。

(メソッドの中身を気にせず、仕様を元に「このメソッドはこうあるべき」みたいなテストを書くことをブラック ボックス テストと言います。一方、Pexのように、中身を想定して、はまりがちなテスト ケースを書くことをホワイト ボックス テストと言います。)

Moles

Pexの付属品なんですが、標準ライブラリ内のメソッドを自作のものに置き換えてしまう機能を提供するツールです。

例えば、DateTime.Nowのような、常に違う値を返すメソッドを、所望の値を返すように書き換えてしまうというような使い方ができます。

Moq

動的処理の回で書きましたが、テストのお供にモック(mock: 模造品)生成フレームワークがあります。

代表例はMoqなどです。

それでもクロス ターゲット

21 12月

Write once, run anywhereだって?まずはそのふざけた幻想をぶち殺す!

 

まあ、全部は無理です、全部は。ただ、だからこそ、どこでも使えるコードと、どこでもは使えないコードは分けて考えましょう。

昨日は、UI層(ビュー)からロジックを分離しましょうという話をしましたが、ビューがrun anywhereしにくい部分だからというのもあります。

ロジック部分の分離

前に、こんな絵を描きました。

この絵の趣旨は、

  • 処理の中核的なロジック部分は、GUI部分とは分離して作りましょう。
  • 文字ベースでも使えるように作るのがコツです。

という話でした。

こうして分離した純粋なロジック部分は、かなりクロス ターゲットに作れるはずです。

いっそ、プロジェクトを分けて作ってしまうのがおすすめです。

これくらい徹底しないと、ほっとくとすぐにビューやコントローラーにロジックを書いちゃう人が出てきます。何度言ってもすぐに。

 

ちなみに、C#以外への移植が必要な場面(あるいはその逆、他言語からC#への移植)もあるかと思います。その場合でも、中核ロジックだけきっちり分離してある方が、移植は楽でしょうねぇ。

“ターゲット”

“クロス”の話をする前に、クロスにしたい”ターゲット”についてさらっと。いくつかの視点があります。

  • プラットフォーム
    • OSやデバイス、何を使うか
  • マウス/タッチ操作か、スクリプトか
    • わざわざ覚えなくても使いやすいのはGUI
    • 操作を記録しておいたり、一括処理したりしやすいのはCUI
  • サーバーかクライアントか
    • どちら側で処理するか

当たり前ですが、ターゲットが変われば書かなきゃいけないコードも変わります。しかし、そのターゲットごとに書かなきゃいけないコードと、どれでも共通して使えるコードはしっかり分けて作りましょう。

例え、当初予定として、単一のターゲットしか予定になかったとしてもです。

  • 予定は変わるものです
  • 普段から癖をつけておくことで、必要になった時にあわてないようにしましょう
  • これが意識できれば、自然とテストしやすいコードにもなります

 

ロジックの分離の効用

いくつか、効用が得られる状況を見ていきましょう。

UIは陳腐化が激しい

まあ、デバイス自体も変わりますから。この数年、ここまで急激にスマホが伸びると思っていなかった人も多いんじゃないでしょうか。気が付いたらコロッと変わっています。

当然、既存アプリをスマホに対応させてくれなんて要望も多いんじゃないでしょうかねぇ。予想できない勢いで、そういう要望がどんどん出てきます。

そして、UI層まで含めたクロス プラットフォーム開発が現実的でないとなると、ロジックをきっちり分離しておかないと痛い目を見ます。

ユーザーの手動操作ばかりじゃない

システムに、プログラム的にアクセスしたい場合も多いです。

  • CUI提供
  • サービス化

CUI提供

まず、CUIに関してですが、CUIの回で説明した通り、サーバー アプリなどでは、GUIとCUIを両方提供することが望まれます。今、おすすめなのは、PowerShellのCmdlet化してしまうことです。

サービス化

サービス化というのは、システム全体を1枚板に作るんじゃなくて、部品ごとに切り分けて作ってプログラム的に呼べるようにしておくことです。それぞれの部品を「サービス」と呼びます。代表的なのは、ウェブ越しのサービスを呼べるようにしておく「ウェブ サービス化」でしょう。今だと、いわゆるREST APIにしておくのが無難だと思います。

前述の通り、UIは陳腐化が激しく、割と頻繁に作り直しや追加の要件がでます。そして、その時に、元と同じ開発者を使えるとは限らないのです。部品の切り出しをしておかないと、引き継ぎのコストも跳ね上がります。

あるいは、システム開発会社に依頼するほどでもない、ほんのちょっとしたツールが欲しくなることもあるでしょう。サービスAPIがちゃんと公開されていたら、案外内製も簡単だったりします。

ほぼ自動

CUI提供にしろサービス化にしろ、中核ロジックをきっちりライブラリ化してあれば、作るのは非常に簡単です。コマンドや、ウェブ サービスAPIのURLと、プログラム コードの間でかなり単純なマッピングをするだけです。

というか、このマッピング作業は完全自動化できるレベルです。

単体テスト

ロジックの分離は、テストのしやすさにも影響します。前節の通り、プログラム的に扱いやすいようになっているはずなので、テストの自動化もしやすいです。

そして、ロジックを分けましょうの回で説明した通り、テストをしないと死にます。あとから確実に泣きをみます。

サーバーとクライアント

このご時世、処理はサーバー上で行って、クライアントは表示だけにすることも多いです。

ところが、常にネットにつながっているわけじゃない。特に、最近はスマホが流行りですし、通信状況がコロコロ変わります。また、些細なことでいちいちサーバー上に問い合わせていたら、応答性が悪くてストレスフルになったり、帯域圧迫したりという問題もあるでしょう。

そこで、特にゲームなんかだと多いんですが、サーバーと同じ処理をクライアント上でも行うことがあります。ここで1つ注意点としては、処理をクライアント側に移すんじゃなくて、両方で2重に処理する必要があります。クライアント上で計算した結果はあくまでキャッシュ的な扱いで、本当にサーバー側と不整合がないか、どこかで同期をとる必要があります。あるいは、ゲームだと深刻な問題になりますが、クライアントだけで処理してしまうと、いわゆるチート プレイがし放題です。

こういうとき、どちらでも使える状態でライブラリ化してあれば、何の苦労もありません。単一コードを、2つのプロジェクトから参照するだけです。

まあ、現実は…HTMLアプリとかでクライアントがJavaScriptになってしまうと、その夢はもろくも崩れ去るわけですが…。とはいえ、前述の通り、移植をするにしても、ロジックを分離してる方が楽です。

 

少し余談。ひどいものだと、仕様書もくそもなくて、「Flashが仕様みたいなものだから、Flash開発担当の人に聞いて同じものを実装して」みたいな話も…ざらに。

まとめ

いろんなターゲットがあります。

  • UI技術の入れ替わり
  • GUIかCUIか
  • サーバー処理かクライアント処理か

UIまで含めたクロス ターゲットは非現実でも、中核となる処理はだいたいどのターゲットでも動くはず、とういか、動くように作れるべき。

テストもしやすくなりますし、もし別の言語に移植する必要があるときも、ずいぶん楽になると思います。

どのターゲットに対しても使える処理は、独立したライブラリとして作ることをお勧めします。

 

補足

今回も何点か補足。

Portable Class Library

.NET Frameworkといっても、参照可能なクラス ライブラリの範囲が変わる、プロファイル(profile)というものを何種類か持っています。

どのプロファイルからでも使えるライブラリを作るためには、Portable Class Libraryという特別なプロジェクトを作る必要があります。

今回説明したような、「中核ロジックを独立させたライブラリ」を作るなら、このPotable Class Libraryを使うのがいいでしょう。

C#→JavaScript

Googleは結構、JavaからJavaScriptコードを生成してるんでしたっけ?まあ、ですよねー。

.NET界隈でも、C#からJavaScriptコードを生成するようなツールが結構あります。有名なものでは、Script#など。

サービスのメタデータ交換

サービス化するということは、ある誰かがサービスを作って、別のある誰かがそのサービスを参照することになります。そして、通常、両者がソースコードを共有することはないので、「ソース コード → サービスAPI化」のマッピングとは逆に、「サービスAPI → ソース コード」のマッピングもほしかったりします。

そのために、「このサービスはこんなAPI、こんなデータを公開しています」というような情報(メタデータ)も公開したいです。ちゃんとこのメタデータが公開されていれば、サービスの利用側が非常に楽になります。

ロジックを分けましょう

20 12月

本日はちょっとしたホラー話です。警告しても警告してもなくならないエンドレスな物語。

エターナル フォース ブリザード!

効果: プロジェクトは死ぬ

ビューとモデルの分離

みなさん、ちゃんとビューは薄く作っていますか?ビューやコントローラーにロジックを詰め込んじゃっていませんか?

まして、.aspx/.cshtml/.php/.jsp なんかのHTMLテンプレート部分にロジックを書いちゃっていませんか?

まあ、「ビューとモデルの分離」と言われてわかる人は、そりゃ分離しますわね。問題は、言われても何のことかわからない人。というか、わかってない人がプロジェクトに含まれている場合。

HTMLテンプレート中のロジック

ASP.NET MVCのRazorエンジン、便利ですね。軽くもう、C#スクリプトですよこれ。C#書き放題!

とかやってて後から泣くわけですね。例えば、以下のようなコードを書いて。

@using System.Linq;
@{
    ViewBag.Title = "
ホーム ページ";
}

<p>
@{
    Sample[] items = ViewBag.Items;
    var i = items.First();
    if (i != null)
    { 
        <span>@i.Name</span>
    }
}
</p>

「Itemsの先頭要素が、あれば表示したい」というつもりのコードです。つもり。LINQに慣れた人なら気づくと思うんですが、Itemsが0要素の時、このコードは例外を起こして止まります。よく見るあれ、

アプリケーションでサーバー エラーが発生しました。

あれが起きます。

問題はこの行ですね。

    var i = items.First(); // Firstは、要素がないとき例外発生

正しくは、FirstOrDefaultって書かないとダメです。

さらに悪いことに

「こんなのすぐに気づくよ」と思うかもしれません。しかし…これの悲劇は…

以下のような状況を想像してください。

  • このコードは自分で書いたものではありません
    • バージョン管理をしていて、updateをしたときに、他人の書いたコートが混ざったものです
  • ローカル実行では問題が出ませんでした
    • たまたま、自分の使っていたテスト データは要素0なデータが含まれていませんでした
  • ところが本番でだけエラーが出ました
    • 再現条件がわからず、小一時間悩むことに
    • そしてしばらくしてから気づく「あれ、私こんなコード書いたっけ?」

とかいうのが起こりうる現実。

問題と対策

Firstメソッドの仕様がどうとか以前の問題で、テストの自動化の習慣がないことが最大の問題ですね。あと、レビューをきっちり回すプロセスの欠如。

そして、テストの習慣がついてたら、cshtml中にロジックを書くのなんてもってのほか。

GUIのテストは難しい

一般に、GUIはテストがしにくいです。Coded UIテスト(プログラムでで「ボタンを押した」とかのイベントを起こして、ユーザー操作を疑似的に再現してUIのテストをする手法)なんかもなくはないですが。多くの場合、手作業確認になります。

前述の、「0要素の場合の確認が漏れ、本番環境でエラー」なんかも、手作業確認だから漏れるわけです。

あくまでテンプレート

cshtmlなどのファイルは、あくまでHTMLを生成するためのテンプレートです。

デスクトップ アプリに関してはこのAdvent Calendarでもちょこっとだけ話しましたが、以下のような、データ バインディングという仕組みを使って、UIからデータを分離します。

UI側の記述では、「ここにXを表示したい」というような印だけを入れておきます。

cshtmlでも、やるべきことは同じで、この手の印にあたるコードだけを書くべきです。

となると、書けることは限られてきます。cshtml中に残るC#コードは以下の程度なはずです。

  • 一覧表示のためのforeachステートメント
  • プロパティ参照(@item.Xなど)
  • ごくごく簡単な分岐(例えば、0の時だけフォントや文字を変えたいとか)

そしてテストを

分離したデータの側は、単体テストを掛けれるはずです。掛けなきゃいけません。

今回話したようなちょっとしたコードですら、後から悩まされるわけです。テストなしで自信を持って書き換えられますか?

性能問題が出ているコードで、「たぶんここ直せば改善しそう。コード直すのは30分ほどなんですけども。テストがないからちょっと触れないですねぇ。」というもよく聞く話。納期が近づいてからでは遅いんです。最初から、テストを書く習慣を身に着けておかないと。

「スピードが大事なんでテストをしっかりやる時間がないんです」とか言って手を抜いて、あとから苦しむことになって、結局余計に時間がかかるなんてのもよく聞く話。よく聞く話で、よく警告話も耳にするのに、なぜか繰り返される悲劇。

まとめ

  • テストがないと後で死ぬ
  • ビューにロジック書くとテストできなくて死ぬ

Visual Studioを使ったテストの話も、残りのAdvent Calendarのどこかでしましょうか。