C#と諸々

C#がメインで他もまぁ諸々なブログです
おかしなこと書いてたら指摘してくれると嬉しいです(´・∀・`)
つーかコメント欲しい(´・ω・`)

2009/01/29 18:30
フィールドごとに getter/setter を用意するな - 予定は未定Blog版
OOコード養成ギブス - rants


最近、パブリックプロパティ (もしくはパブリックフィールド) の善悪について悩んでます。
例えば、ドメインオブジェクトを UI に表示する処理って、ドメインオブジェクトに持たせるわけにはいかないですよね?
かといって、DI で UI 表示処理を注入するってのも現実的じゃないと思います。

で、ドメインオブジェクトにパブリックプロパティを用意するとこうなります。

public sealed class Book
{
    public Book(string id, string title)
    {
        this._id = id;
        this._title = title;
    }

    private readonly string _id;
    private readonly _title;

    public string Id { get { return this._id; } }
    public string Title { get { return this._title; } }
}

public sealed class BookDetail : Page
{
    protected void Page_PreRender(object sender, EventArgs e)
    {
        this._idTextBox.Text = this._book.Id; // Book インスタンスの持ってき方はてきとー
        this._titleTextBox.Text = this._book.Title;
    }
}

でもこれだと、後から Book に出版社名を追加した時、BookDetail の修正漏れが発生する可能性があります。
そこで、こんな風にしてみます。

public sealed class Book
{
    public Book(string id, string title)
    {
        this._id = id;
        this._title = title;
    }

    private readonly string _id;
    private readonly string _title;

    public void Present(Receiver receiver)
    {
        receiver(this._id, this._title);
    }

    public delegate void Receiver(string id, string title);
}

public sealed class BookDetail : Page
{
    protected void Page_PreRender(object sender, EventArgs e)
    {
        Book.Receiver receiver = delegate(string id, string title)
        {
            this._idTextBox.Text = id;
            this._titleTextBox.Text = title;
        };
        this._book.Present(receiver); // Book インスタンスの持ってき方はてきとー
    }
}

後から Book に出版社名を追加したら、Receiver デリゲートのシグネチャが変わるため、BookDetail を修正せずにコンパイルを通すことはできません。

でもホントにこっちの方がいいんだろーか。
タイトルだけを表示に使用する画面でも、(タイトルの getter はないので) Present メソッドを使用してタイトルを受け取る必要があります。
それでも良いような気もするし悪いような気もします。

あ、Identity (Book なら ID) に関しては、なんとゆーか独立した存在価値 (?) みたいなものを持っているので、どちらにせよパブリックプロパティを用意 (もしくはパブリックフィールド化) して良いと思います。


// 追記
Java で似たようなコード書いてみました。

public final class Book {
    public Book(String id, String title) {
        this.id = id;
        this.title = title;
    }

    private final String id;
    private final String title;

    public void present(Receiver receiver) {
        receiver.receive(this.id, this.title);
    }

    public interface Receiver {
        void receive(String id, String title);
    }

    public static void main(String[] args) {
        Book book = new Book("1234", "aiueo");
        Book.Receiver receiver = new Book.Receiver() {
            public void receive(String id, String title) {
                System.out.println(id);
                System.out.println(title);
            }
        };
        book.present(receiver);
    }
}


// 2009/02/02
Visitor より Receiver の方がいい感じなので変更。


難しいですよね・・・
GUI や DB まわりだと、どうしても getter (setter もか) が欲しくなります。
僕は「値をまとめることを目的としたクラスはイミュータブルにし、getter のみ可」という指針でやってます。
internal な setter は実は結構良く使います^^;

2009.01.30 12:54 URL | bleis-tift #sqCyeZqA [ 編集 ]


すみません、返信遅くなりました m( _ _;)m


> GUI や DB まわりだと、どうしても getter (setter もか) が欲しくなります。

そうそう、GUI と DB 周りなんですよね。で、そこで必要になるのは大抵 "全て同時に" なんですよねぇ。


> 僕は「値をまとめることを目的としたクラスはイミュータブルにし、getter のみ可」という指針でやってます。

"値をまとめる" ってゆーと、つまりバリューオブジェクトのことですかね。(もうちょい広範な意味かな)

2009.02.01 22:27 URL | よこけん #Ay6tTHf6 [ 編集 ]


> "値をまとめる" ってゆーと、つまりバリューオブジェクトのことですかね。(もうちょい広範な意味かな)
バリューオブジェクトよりも広範ですね。
DTO の代替というか、setter なし DTO というか、そんな感じです。

2009.02.04 12:34 URL | bleis-tift #sqCyeZqA [ 編集 ]












トラックバックURL↓
http://csharper.blog57.fc2.com/tb.php/251-1cb0c75d