概要
オブジェクト指向においては、内部の値を直接変更、参照することを避けるようにということはよく言われる。
あるクラスのメンバへの代入、参照を許さず、メソッドを通して代入、参照するようにする。
そのとき、どうやって値を設定・取得するようにするべきか考えてみる。
仮で下記のようなIndexerのみのクラスを使用して色々試してみる。
public class Indexer
{
public string this[string str]
{
get
{
/// 本来は何か処理をして返す
/// 今回はそのまま引数の文字列を返す
return str;
}
}
}
このクラスを使用する側はNameという文字列とLowerAddressというAddressを小文字化した文字列をインスタンスから引き出せるようにする。
ざっくりパターン
Constructorで全ての値を取得する
まず最初に考えるのがコンストラクタ内で全て値を入れておくパターン。
public class ContructorSet
{
public string Name { get; set; }
public string LowerAddress { get; set; }
public ContructorSet(Indexer indexer)
{
Name = indexer["Name"];
LowerAddress = indexer["Address"].ToLower();
}
}
プロパティであるもののメンバと変わらない。
今回であればあまり行数を必要とするコードではないので大丈夫だが、場合によってはコンストラクタ内のコードが大きく膨れ上がる可能性がある。
各設定する値に対応したメソッドを作成し、コンストラクタ内で設定する
コンストラクタ内の行数を少なくするためにメソッドに切り出してみた。
public class GetterMethod
{
public string Name { get; set; }
public string LowerAddress { get; set; }
public GetterMethod(Indexer indexer)
{
Name = GetName(indexer);
LowerAddress = GetLowerAddress(indexer);
}
private string GetName(Indexer indexer)
{
return indexer["Name"];
}
private string GetLowerAddress(Indexer indexer)
{
return indexer["Address"].ToLower();
}
}
これであれば行数が増えようと大丈夫。
ただし、インスタンスがただのデータ置き場のようになっている。
加工する元の値をインスタンスで保持し、メソッドで加工・参照する
privateなメンバに、加工に必要な値を保持し、それを使用して各メソッドで求める値を加工・参照する
public class GetterMethod2
{
private Indexer indexer;
public GetterMethod2(Indexer indexer)
{
this.indexer = indexer;
}
public string GetName()
{
return indexer["Name"];
}
public string LowerAddress()
{
return indexer["Address"].ToLower();
}
}
必要な値のみ保持し、必要な時に都度加工した値を参照できるようになった。
Getterプロパティ内で行う
C#ではGetterプロパティが使用できるのでメソッドではなくインスタンスのプロパティとしてアクセスできる形式に変えてみた。
public class GetterProperty
{
private Indexer indexer;
public GetterProperty(Indexer indexer)
{
this.indexer = indexer;
}
public string Name
{
get
{
return indexer["Name"];
}
}
public string LowerAddress
{
get
{
return indexer["Address"].ToLower();
}
}
}
かなりスマートになってきた気がする。
この形式であればSingletonなんかも容易に行うことができる。
public class SingletonGetterProperty
{
private Indexer indexer;
public SingletonGetterProperty(Indexer indexer)
{
this.indexer = indexer;
}
private string name;
public string Name
{
get
{
if (name == null)
{
name = indexer["Name"];
}
return name;
}
}
private string lowerAddress;
public string LowerAddress
{
get
{
if (lowerAddress == null)
{
lowerAddress = indexer["Address"].ToLower();
}
return lowerAddress;
}
}
}
加えてテストコードも書きやすい。
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ClassSample.Tests
{
[TestClass]
public class GetterPropertyTest
{
private GetterProperty getterProperty;
[TestInitialize]
public void Initialize()
{
var indexer = new Indexer();
getterProperty = new GetterProperty(indexer);
}
[TestMethod]
public void NameTest()
{
Assert.AreEqual("Name", getterProperty.Name);
}
}
}
終わり
基本的にコンストラクタで設定された値は最低限の加工でインスタンスのprivateなメンバとして保持し、各メソッド、プロパティで加工・参照を行う形がよさそう。