【オブジェクト指向設計】クラスのgetter禁止について考えてみる

本記事では、クラスのgetterメソッドの禁止について考えてみます。

結論は、

getterメソッドは極力使用しない。ただし、コードが分かりにくくなるなら、getterメソッドを使用する。

です。

getter禁止にすることは大事ですが、getter禁止にこだわりすぎないようするべきです

目次

getter禁止のメリット

ThoughtWorksアンソロジーのオブジェクト指向エクセサイズにて以下のように明記されています。

5.2.10 ルール9:Getter、Setterプロパティを使用しないこと

ThoughtWorks アンソロジー

本記事はgetterのみ考えていきます。

getter禁止にする目的は、↓のことが挙げられると思います。

getter禁止のメリット
  • データに対する処理を1か所にまとめられる
  • クラスの役割が明確になる
  • コードが分かりやすくなる
  • ドメイン貧血症の防止

getter禁止のコード

実際のコードでgetter禁止を実装してみます。

言語C#、座標クラスCord を題材にします。メンバにX座標、Y座標を持つクラスです。

namespace GetterNG
{
    internal class Cord
    {
        /// <summary>X座標</summary>
        private int x;

        /// <summary>Y座標</summary>
        private int y;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="x">X座標</param>
        /// <param name="y">Y座標</param>
        public Cord(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }
}

指定座標分移動するメソッドを作成します。

namespace GetterNG
{
    internal class Cord
    {
        /// <summary>
        /// 移動する
        /// </summary>
        /// <param name="src">移動分の座標</param>
        /// <returns>移動後の座標</returns>
        public Cord Move(Cord src)
        {
            int dstX = x + src.y;
            int dstY = y + src.y;
            return new Cord(dstX, y + dstY);
        }
    }
}

このコードは、移動分座標の加算処理を1箇所にまとめています。getterメソッドを使う場合は、加算処理をCordクラス の外でやることになります。

加算処理だけだとメリットは薄いかもしれませんが、回転処理だとどうなるでしょうか?

namespace GetterNG
{
    internal class Cord
    {
        /// <summary>
        /// 回転する
        /// </summary>
        /// <param name="center">中心の座標</param>
        /// <param name="deg">回転角度</param>
        /// <returns>回転後の座標</returns>
        public Cord Rotate(Cord center, float deg)
        {
            float cos = (float)Math.Cos((float)deg * Math.PI / 180);
            float sin = (float)Math.Sin((float)deg * Math.PI / 180);

            int dstX = (int)(cos * (x - center.x) - sin * (y - center.y) + center.x);
            int dstY = (int)(sin * (x - center.x) + cos * (y - center.y) + center.y);

            return new Cord(dstX, dstY);
        }
    }
}

移動より計算ロジックが複雑になりました。

getterメソッドを使う場合は、この計算ロジックを外で実装しなければならなくなります。あちこちで計算ロジックを実装すると、コードが見にくくなります。修正時の影響範囲も大きくなります。

この計算ロジックは、座標クラスの役割として、メソッドに置いておくほうがよいです。座標クラスの役割がより明確になります。

IDE のgetter自動生成は使わない

IDE にgetterの自動生成の機能があります。

これは、使用しないほうがいいです

プログラミングを始めたばかりだと、入門書などにgetterを使っているので、getterを使うことに慣れてしまいます。

IDE のgetterを使うことが正しいことと錯覚させられます

書籍でも、ネットでも、本記事でも、getter禁止に関してはいろいろ議論があります。何も考えずにgetterの自動生成はやるべきではないです。getterを使うときはより慎重になるべきです。

getterを使う場合

それでもgetterを使う場合はあります。例えば、C# でXMLファイルを読み込むときです。

↓の記事でXmlSerializer を使う場合は、getterを使用します。

[Serializable]
public class ItemXml
{
    [XmlElement("data2")]
    public string data2 { get; set; }

    [XmlElement("data3")]
    public float data3 { get; set; }

    public ItemXml()
    {
        data2 = "";
        data3 = -1;
    }
}

XmlSerializerの仕様として、ItemXMLファイルはgetter を使用しなければなりません

この場合は、getterを使用したほうがよいです。

getterを使用しないために、XmlSerializerを使用しないというのは、違和感があります。getter禁止は、クラスの役割を明確にし、コードを分かりやすくする目的もあります

XmlSerializerを使用しないことで、コードが分かりにくくなるのは避けるべきです。

このケース以外にもドメイン駆動設計では、DTO などでgetterを使った方がコードが分かりやすくなることがあります。その場合は、getterを使いましょう。

getter禁止のメリットと相談して、どうするかは考えたほうがよいです。

まとめ

クラスのgetterメソッドの禁止について考えてみました。

結論は、getterメソッドは極力使用しないようにすることです。

ただし、getterを使った方がコードがより分かりやすくなることもあります。そのときは、getterを使用します。

getter禁止にこだわりすぎないようにすべきです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次