調べ物した結果

現役SEが仕事と直接関係ないことを調べた結果とか感想とか

Json.NETとオブジェクト指向の付き合い方がよくわからなくなったから調べてみたら悲しい結果にたどり着いた

ことのはじまり

・コードレビュー中、手元のオブジェクト指向の参考にしている本に「なるべく不変にしろ。」とあり、

 public string hoge {get; set;}

じゃなくて

 public string hoge {get; private set;}

にしといてとりあえずセッターは外に出さないようにしたほうがよさげ。と聞いてみたら
JSONで扱う可能性があって、privateにするとちゃんと引き渡せないから却下」と言われてひとまず納得。
でもそうしたら「オブジェクト指向」と「JSON」相性わるすぎじゃん。はーん?となり、
解決策がないか。それともこの手元のオブジェクト指向参考資料がまずいんか?JSONの知識が足りてなさすぎるのか???ひょぇえええええってなったから調べた。

持ってる知識、前提、困ってるところ

・プロジェクトの都合上?。WEBサーバ、クライアント間でデータを受け渡すのにJSON形式で受け渡してる。(シリアライズからのデシリアライズ
JSONシリアライズ(デシリアライズ)の特性上、基本的にはpublicなプロパティしか相手にしてくれない(らしい)。privateで隠蔽できない。
・手元のオブジェクト指向の参考にしている本には「なるべく不変にしろ。」ただしロジックは「寄せろ」と書いてる。どうせいっちゅうねん。
 巷でも絶賛されてるし、少なくとも試してる限り本はあってると思う。あとは僕の解釈が間違ってる可能性もあるが・・・

解決

・普通にできたん・・・えぇ・・・

    public readonly string hoge

でも

    public string DrugName { get; private set; }

でも難なくOK.

JSONの性質のところ(JSONがどうのこうのっていうより、そらPrivateスコープなんだからそうよね。という気がしなくもない)

サンプル的につくった。受け渡したいのはこの子。薬剤の名称と数量を持ってるだけ。

    public class Prescription
    {
        //本当はこいつらを readonlyとか、privateにしときたい(不変にする)
        public string DrugName;
        public int Quantity;

        public Prescription(string drugname, int quantity)
        {
            this.DrugName = drugname;
            this.Quantity = quantity;
        }

        public string DisplayText()
        {
            return DrugName + " " + Quantity.ToString() + "回";
        }
    }

WEB側でこいつの1データを返却するとする

    public static class ServerSideHoge
    {
        // Json形式で引き渡すのだ!
        public static string GetDBData()
        {
            var pres = new Prescription("アーガメイト", 3);
            return JsonConvert.SerializeObject(pres);
        }
    }

Client側で上の処理を呼び出して、出力する。

    public class ClientSideHoge
    {
        public void WriteDrugName()
        {
            // Json形式で受け取る!
            string json = ServerSideHoge.GetDBData();
            Prescription pres = JsonConvert.DeserializeObject<Prescription>(json);

            Console.Write(pres.DisplayText());
        }
    }

Web(ServerSideHoge.GetDBData()の部分)からは「{"DrugName":"アーガメイト","Quantity":3}」というよさげな文字列が返却されて、
Console.Writeで吐き出されるのは「アーガメイト 3回」という文字が返ってくる。

ところが下のように例えばPrivateで宣言してしまうと。。。

    public class Prescription
    {
        //public string DrugName;
        private string DrugName;
        //public int Quantity;
        private int Quantity;

        public Prescription(string drugname, int quantity)
        {
            this.DrugName = drugname;
            this.Quantity = quantity;
        }

        public string DisplayText()
        {
            return DrugName + " " + Quantity.ToString() + "回";
        }
    }

Web(ServerSideHoge.GetDBData()の部分)から帰ってきた文字は「{}」という悲しい結果。
そのため、Console.Wirteで吐き出されるのは「 0回」という文字。ぐぬぬってのが「private」だと・・・という誤解ポイント。だとおもってた。

上でも書いたが、
これでもちゃんと受け取れたし、

        public readonly string DrugName
        public readonly  int Quantity

これでも受け取れた。

        public string DrugName { get; private set; }
        public int Quantity { get; private set; }

とれたはとれたんだけど、この辺で気付いてしまった。

追加の加

ここまではよかったんだけど、よく考えたらうちのプロジェクトで扱ってるメンバーが必ずコンストラクタで設定されるなんて、
ほぼほぼ奇跡。なので、Prescriptionを以下のように拡張してみた。

    public class Prescription
    {
        public string DrugName { get; private set; }
        public int Quantity { get;  private set; }
        // 「不変」が守られてなくて外から変更できちゃうやつ
        public string GeneralName { get; private set; }

        public Prescription(string drugname, int quantity)
        {
            this.DrugName = drugname;
            this.Quantity = quantity;
        }
        // こいつのせいで「不変」が崩れる。
        public void CreateGeneralName()
        {
            GeneralName = "一般名)" + DrugName;
        }
        public string DisplayText()
        {
            // Generalのほうを使うように変更
            return GeneralName + " " + Quantity.ToString() + "回";
        }
    }

Webの返却結果は「{"DrugName":"アーガメイト","Quantity":3,"GeneralName":"一般名)アーガメイト"}」
おぉ・・・なんだ気のせいか。
ほらちゃんとConsole.Wirteで吐き出されるのは・・・「 3回」。ほげぇ。
シリアライズのタイミングで欠けてらっしゃる・・・死。
f:id:couraeg:20190419000728p:plain

ええっとつまり・・・

・ちゃんとオブジェクト指向して「不変」を守っておかないとこうなってしまうよ。
・一回やぶられたオブジェクト指向はさらなる悲劇を生むよ(private設定できまっせーんwww)

悲しみ。