ゲッターを使用して定数を回避するのは良い習慣ですか?


25

クラスの外部で使用されている定数をゲッターで置き換えるのは良い習慣ですか?

例として、if User.getRole().getCode() == Role.CODE_ADMINまたはを使用する方が良いif User.getRole().isCodeAdmin()でしょうか?

それはこのクラスにつながります:

class Role {
    constant CODE_ADMIN = "admin"
    constant CODE_USER = "user"

    private code

    getRoleCode() {
       return Role.code
    }

    isCodeAdmin () {
       return Role.code == Role.CODE_ADMIN
    }

    isCodeUser () {
       return Role.code == Role.CODE_USER
    }
}

15
のようなものを使いたいUser.HasRole(Role.Admin)です。
CodesInChaos


4
Tell Do n't Ask原則を確認してください。
アンディ

私は前提に疑問を抱いています:User.getRole().getCode()すでに不快な読みであり、コードをロールと比較すると、それはさらに不格好になります。
msw

回答:


47

まず最初に、デメテルの法則にentity.underlyingEntity.underlyingEntity.method()よると、次のようなことを行うことはコードのにおいと見なされます。このようにして、実装の詳細の多くを消費者に公開しています。そして、そのようなシステムの拡張または修正の各ニーズは多くのことを傷つけます。

そのため、CodesInChaosのコメントに従って、HasRoleまたはのIsAdminメソッドを使用することをお勧めしますUser。このように、ユーザーにロールを実装する方法は、コンシューマの実装の詳細のままです。また、ユーザーに自分の役割の詳細について尋ねてから、それに基づいて決定するのではなく、自分の役割が何であるかをユーザーに尋ねる方が自然です。


string必要な場合を除き、sの使用も避けてください。内容は事前に不明であるため、変数のname良い例ですstring。一方、roleコンパイル時によく知られている2つの異なる値がある場合は、厳密な型指定を使用することをお勧めします。それが列挙型の出番です...

比較する

public bool HasRole(string role)

public enum Role { Admin, User }

public bool HasRole(Role role)

2番目のケースでは、何を渡すべきかについて、より多くのアイデアを得ることができます。またstring、ロール定数がわからない場合に誤って無効な値を渡すことを防ぎます。


次に、ロールの外観を決定します。ユーザーに直接保存されている列挙型を使用できます。

public enum Role
{
    Admin,
    User
}

public class User
{
    private Role _role;

    public bool HasRole(Role role)
    {
        return _role == role;
    }

    // or
    public bool IsAdmin()
    {
        return _role == Role.Admin;
    }
}

一方、ロールに動作自体を持たせたい場合は、そのタイプがどのように決定されているかの詳細を再び隠す必要があります。

public enum RoleType
{
    User,
    Admin
}

public class Role
{
    private RoleType _roleType;

    public bool IsAdmin()
    {
        return _roleType == RoleType.Admin;
    }

    public bool IsUser()
    {
        return _roleType == RoleType.User;
    }

    // more role-specific logic...
}

public class User
{
    private Role _role;

    public bool IsAdmin()
    {
        return _role.IsAdmin();
    }

    public bool IsUser()
    {
        return _role.IsUser();
    }
}

ただし、これは非常に冗長であり、ロールを追加するたびに複雑さが増します。通常、デメテルの法則を完全に順守しようとすると、コードが最終的にこのようになります。モデル化するシステムの具体的な要件に基づいて、設計を改善する必要があります。

あなたの質問によると、enumを直接オンにした最初のオプションを使用した方が良いと思いますUser。でさらにロジックが必要なRole場合は、2番目のオプションを開始点として検討する必要があります。


10
私はこれが人々がどこでもそれをするであろう方法であったことを望みます。他の属性と等しいかどうかを確認するためだけに、privatr属性にゲッターを設定するのは、とてもひどい習慣です。
アンディ

2
「instance.property.property.method()...」については、それは流動的なものではありませんか?
ピーターモーテンセン

2
@PeterMortensen流fluentなインターフェイスとは異なります。型の流interfaceなインターフェイスをX使用すると、のような関数呼び出しの文字列を作成できますX.foo().bar().fee()...。この流interfaceなインターフェイスでは、foo、bar、およびfeeはすべてX、typeのオブジェクトを返すクラス内の関数になりますX。しかし、この例で言及されている 'instance.property.property.method()の場合、2つのproperty呼び出しは実際には別々のクラスになります。ここでの問題は、低レベルの詳細を把握するためにいくつかの抽象化レイヤーを通過していることです。
シェーズ

10
良い答えですが、デメテルの法則はドットを数える練習ではありません。 instance.property.property.method()必ずしも違反ではなく、コードの匂いでさえありません。オブジェクトとデータ構造のどちらで作業しているかによって異なります。 Node.Parent.RightChild.Remove()おそらくLoDの違反ではありません(ただし、他の理由で臭いがします)。 var role = User.Role; var roleCode = role.Code; var isAdmin = roleCode == ADMIN;私は常に「1つのドットのみを使用した」という事実にもかかわらず、ほぼ間違いなく違反です。
カールレス

1
私はそれinstance.property.property.method()がLoDの違反であることを理解してinstance.method().method()いますが、OPには問題ないはずです。最後の例では、非常に多くの定型コードがあり、これはUserのファサードとしてのみ機能しますRole
ベルギ

9

格納されているコードが管理者コードであるかどうかをチェックする機能を用意するのは無難なようです。本当に知りたいのは、その人が管理者かどうかです。したがって、定数を公開したくない場合は、コードがあることも公開してはならず、メソッドisAdmin()およびisUser()を呼び出してください。

つまり、「User.getRole()。getCode()== Role.CODE_ADMINの場合」は、ユーザーが管理者であるかどうかを確認するためだけに本当に役立ちます。開発者はその行を書くためにいくつのことを覚えなければなりませんか?ユーザーにはロールがあり、ロールにはコードがあり、Roleクラスにはコードの定数があることを覚えておく必要があります。これは、純粋に実装に関する多くの情報です。


3
さらに悪いことには、ユーザーには常に1つの役割があり、それ以上でもそれ以下でもありません。

5

他の人が既に投稿したものに加えて、定数を直接使用することには別の欠点があることに留意する必要があります。 が変更された場合、それらすべての場所も変更する必要があります。

そして、それは強化することを恐ろしくします。たぶん、あなたがしたいです、ある時点でスーパーユーザータイプ。それは明らかに管理者権限も持っています。カプセル化では、基本的に追加するのは1行です。

短くてきれいなだけでなく、使いやすく理解しやすいです。そして-おそらく何よりも-間違いを犯すのは難しいです。


2

定数を避けてメソッドisFoo()などを使用するという提案には大体同意しますが、考えられる反例の1つです。

ある場合 これらの定数何百もあり、呼び出しがほとんど使用されない場合、何百ものisConstant1、isConstant2のメソッドを記述するのに労力を費やす価値はないかもしれません。この特定の異常なケースでは、定数を使用するのが妥当です。

列挙型を使用するかhasRole()、何百ものメソッドを記述する必要がないので、可能な限り最高のものです。


2

あなたが提示したオプションのどちらも根本的に間違っているとは思いません。

あなたが私がひどく間違って呼ぶ一つのことを提案していないのを見る:Roleクラスの外の関数でロールコードをハードコーディングする。あれは:

if (user.getRole().equals("Administrator")) ...

私は間違いなく間違いだと思います。誰かが文字列のつづりを間違えたために、これを実行してから謎のエラーを受け取るプログラムを見てきました。関数が「ストック」をチェックしていたときにプログラマーが「ストック」を書いたことを思い出します。

100の異なる役割がある場合、可能なすべての役割をチェックするために100個の関数を作成するのは非常に消極的です。あなたはおそらく最初の関数を書いてからそれを99回コピーして貼り付けることでそれらを作成するでしょう、そしてそれらの99のコピーのどれにあなたがテストを更新するのを忘れるか、1つで降りるだろうとどれだけ賭けたいかあなたはリストを駆け抜けたので、あなたは今持っています

public bool isActuary() { return code==Role.ACTUARY; }
public bool isAccountant() { return code==Role.ACTUARY; }
... etc ...

個人的には、呼び出しの連鎖も避けたいと思います。むしろ書きたい

if (user.getRole().equals(Role.FOOBATER))

それから

if (user.getRole().getRoleCode()==Role.FOOBATER_CODE)

そして、その時点でメモを書く理由:

if (user.hasRole(Role.FOOBATER))

次に、ロールを確認する方法をUserクラスに知らせます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.