ク●コード改め「ぴよぴよコード」なのですが、目の前にあるク●コード改め「ぴよぴよコード」をなんとか使いこなすための思考実験。
ええ、思考実験なので業務コードの場合は業務コードにあわせたカスタマイズが必要になる訳ですが。
基本は「リファクタリング」するあるいは「リライト」するのが良いのですが事情が許さないことが多々あります。
- できあがったアセンブリに手を付けてはいけないという縛りがある。
- コードが「ぴよぴよ」すぎて、ロジックが訳わからん。
- リライトしてテストしてという時間がコードが複雑すぎてできない。
「手を付けてはいけない」のほうは、政治的な問題が絡むので、できることならば手を付ける方向に動くようマネジメントすればよいのですが、後の二つはちょっと単純にリファクタリングなりリライト(書き直し、作り直し)などをしている時間が、納期的や能力的(既存のコードを理解するという意味で)がちょっとない時に使います。
まぁ、基本は「動いているものには手を付けない」という古い思想を守るパターンということですね。このあたり、経済的に「古い思想を守る」のが割が合わなくなったら、全面的にリライト、リメイクするわけです。そのあたりの判断基準の話は後日。
■もともとのパターン
サンプルとして作った Piyo クラスがこれです。
public class Piyo
{
public void Plus( int x, int y, ref int ans )
{
// わざわざリファレンスで答えを返している
ans = x + y;
}
public void PlusMuins( int x, int y, ref int ans1, ref int ans2 )
{
// 戻り値が2つあるので、ref で返している
ans1 = x + y;
ans2 = x - y;
}
}
何故か、戻り値を使わずに ref で返しています。2つ答えがある場合は2つの ref を使います。
// ref を多用しているパターン
private void button1_Click(object sender, EventArgs e)
{
Piyo piyo = new Piyo();
int x = 10;
int y = 20;
int ans = 0;
piyo.Plus(x, y, ref ans);
int ans1 = 0;
int ans2 = 0;
piyo.PlusMuins(x, y, ref ans1, ref ans2);
}
統一性が取れているように見えるのですが、オブジェクト指向的にはちょっとアレですよね。特に C# の場合は呼び出し時にも ref をつけないと駄目なので結構面倒です。
C++ の場合は、&ans1 のようにポインタ渡しをすることがよくあるのですが、できればオブジェクトとして独立性を高くしておきたいところ。
■C#の拡張メソッドを使う
拡張メソッド (C# プログラミング ガイド)
http://msdn.microsoft.com/ja-jp/library/bb383977(v=vs.90).aspx
C# 3.0 の頃からある拡張メソッドの機能で Piyo クラスを拡張してしまいます。
// Piyo クラスを拡張する
public static class PiyoExtention
{
static public int Plus(this Piyo piyo, int x, int y)
{
// 戻り値を使うように変更
int ans = 0;
piyo.Plus(x, y, ref ans);
return ans;
}
static public Ans PlusMuins(this Piyo piyo, int x, int y)
{
// 2つの答えはオブジェクトで返す
var ans = new Ans();
piyo.PlusMuins(x, y, ref ans.plus, ref ans.muins);
return ans;
}
public class Ans
{
public int plus;
public int muins;
}
}
PiyoExtention クラスを作るときに多少手間ですが、利用するときには普通のクラスっぽくなるので便利かと。
これは既存のアセンブリに手を付けられないときに活用できます。
// 拡張メソッドを使うパターン
private void button2_Click(object sender, EventArgs e)
{
// ひとつの ref は戻り値にする
var piyo = new Piyo();
int x = 10;
int y = 20;
int ans = piyo.Plus(x, y);
// ふたつ以上ある場合はオブジェクトで返す
PiyoExtention.Ans ans1 = piyo.PlusMuins(x, y);
}
答えが二つある場合は、オブジェクト/構造体で返しています。このあたり、戻り値のデータの受け渡しをオブジェクト経由でやるのか、プロパティ経由でやるのかという2つのスタイルがあるのですが、拡張メソッドの場合はプロパティを作れないのオブジェクトで返します。
強引に、プロパティを作れなくもないのですが、拡張メソッド自体がややこしい作りになるのでパスしたほうが良いかと。
■強引に継承して拡張する
もうひとつのパターンは、クラス自体を継承して拡張する方法ですね。
完璧にやろうとすると、全てのメソッドやプロパティを継承させることになって大変手間なのですが、必要な部分だけを継承して、その他の部分は内部で持っている _piyo オブジェクトをそのまま公開してしまいます。
// ちまちまと継承するパターン
private void button3_Click(object sender, EventArgs e)
{
var piyo = new PiyoEx(new Piyo());
int x = 10;
int y = 20;
int ans = piyo.Plus(x, y);
var ans2 = piyo.PlusMuins(x, y);
int plus = ans2.plus;
int muins = ans2.muins;
}
ここでは、2つ以上の答えを返す場合には、試しに無名オブジェクトを作って dynamic にして返しているのですが、インテリセンスが効かないのであまり多用しないほうがいいかも。
// Piyo クラスを継承する
public class PiyoEx
{
// 他でも使えるように public にしておく
public Piyo _piyo;
// コンストラクタ
public PiyoEx(Piyo piyo)
{
_piyo = piyo;
}
public int Plus(int x, int y)
{
int ans = 0;
_piyo.Plus(x, y, ref ans);
return ans;
}
public dynamic PlusMuins(int x, int y )
{
int ans1 = 0;
int ans2 = 0;
_piyo.PlusMuins(x, y, ref ans1, ref ans2);
// 無名オブジェクトを利用してみる
return new { Puls = ans1, Muins = ans2 };
}
}
ここでは、1つのクラスしか継承していませんが、ごちゃごちゃした 純粋オブジェクト指向 Java 風の 3,4個あるクラスをひとまとめにしてしまって便利クラスを作っておくのも手です。オブジェクト指向的にはどうかという意見もあるでしょうが、業務の質に合わせていくのが「経済的」ですよ、ということで。

「継承する」とか言って、継承していなかったので PiyoEx を継承する形に直しました。継承しないで「くるむ」場合は、余計なメソッドが公開されなくていいんだけど、今回の場合は「公開されているけど、使ってあげない」というのがベター。
あ、間違えた。
本当に継承をしてしまうと、_piyo を使っている意味がないし。。。
この継承のパターンは2つあって
・元の piyo オブジェクトを活かして、PiyoEx で拡張する。
・今後は Piyo クラスは使わずに、PiyoEx クラスを使う。
というパターンがある。で、実務的には先のパターンが多いので、元に戻すことにする。