通过使用getter避免常量是一种好习惯吗?


25

用getter替换在类外部使用的常量是一种好习惯吗?

例如,使用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
查看“不要问”原则。
安迪

User.getRole().getCode()对此前提提出了质疑:已经很不愉快了,将代码与角色进行比较会使它变得更加笨拙。
msw 16年

Answers:


47

首先,请注意,entity.underlyingEntity.underlyingEntity.method()根据Demeter定律,执行类似操作会被视为代码异味。这样,您就可以向使用者公开许多实现细节。而对这种系统的扩展或修改的每一个需求都会伤害很多。

因此,鉴于此,我建议您根据CodesInChaos的注释在上使用HasRoleor IsAdmin方法User。这样,如何在用户上实现角色的方式仍然是消费者的实现细节。而且,向用户询问他的角色而不是向他询问他的角色的详细信息,然后根据该角色来决定,这更加自然。


string除非必要,也请避免使用s。namestring变量的一个很好的例子,因为内容是事先未知的。另一方面,类似role在编译时众所周知的两个不同值的情况,最好使用强类型。那就是枚举类型起作用的地方...

比较

public bool HasRole(string role)

public enum Role { Admin, User }

public bool HasRole(Role role)

第二种情况使我对应该传递的内容有了更多的了解。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();
    }
}

但是,这非常冗长,添加每个角色都会增加复杂性-通常,这就是您尝试完全遵守Demeter法则时代码的结局。您应该根据要建模的系统的具体要求来改进设计。

根据您的问题,我想您最好直接将enum放在第一个选项上User。如果您需要更多的逻辑Role,则应将第二个选项视为起点。


10
我希望这是人们到处都能做到的方式。仅仅为了检查它是否与其他属性相等而在其他属性上使用吸气剂是一种可怕的做法。
安迪

2
关于“ instance.property.property.method()...”这不是很麻烦吗?
Peter Mortensen

2
@PeterMortensen它与流畅的界面不同。流利的类型接口X肯定可以使您进行函数调用字符串,如X.foo().bar().fee()...。在这种流畅的接口中,foo,bar和fee都是类内的函数,X返回type的对象X。但是对于此示例中提到的'instance.property.property.method(),这两个property调用实际上将位于单独的类中。那里的问题是您要经过几层抽象才能掌握底层细节。
Shaz

10
很好的答案,但得墨De耳定律不是点数练习。 instance.property.property.method()不一定是违规甚至是代码气味;这取决于您使用的是对象还是数据结构。 Node.Parent.RightChild.Remove()可能并不违反LoD(尽管由于其他原因而有臭味)。 var role = User.Role; var roleCode = role.Code; var isAdmin = roleCode == ADMIN;尽管我总是“只使用一个点”,但几乎可以肯定这是一种侵犯。
卡尔·莱斯

1
我知道这instance.property.property.method()违反了LoD,但是OP可以instance.method().method()这样做。在您的最后一个示例中,有很多样板代码User只能用作的外观Role
Bergi

9

似乎很愚蠢,无法具有一个功能来检查存储的代码是否为管理员代码。您真正想知道的是该人是否是管理员。因此,如果您不想公开常量,那么也不应公开代码,而应调用isAdmin()和isUser()方法。

就是说,“如果User.getRole()。getCode()== Role.CODE_ADMIN”确实仅用于检查用户是否为管理员。开发人员必须记住多少行才能编写该行?他或她必须记住用户具有角色,角色具有代码,并且Role类具有代码常量。这是很多有关实现的信息。


3
更糟糕的是:用户总是只扮演一个角色,而没有更多或更少的角色。
Deduplicator

5

除了其他人已经发布的内容之外,您应该记住直接使用常量还有另一个缺点:如果发生任何改变您处理用户权限的方式,那么所有这些位置也都需要更改。

而且它使增强变得可怕。也许您希望一次拥有一个超级用户类型,显然也具有管理员权限。有了封装,它基本上是单线添加的。

它不仅简短,干净,而且易于使用和理解。而且-也许是最重要的-很难弄错。


2

尽管我在很大程度上同意避免使用常量和使用方法的建议,但还是有isFoo()一个可能的反例。

如果有数百个这样的常量,并且调用很少使用,那么写数百个isConstant1,isConstant2方法可能不值得。在这种特殊的异常情况下,使用常量是合理的。

请注意,使用枚举或hasRole()避免需要编写数百种方法,因此这是所有可能的方法中最好的。


2

我认为您提出的任何一种选择都不是根本错误的。

我看到您没有提出我会断然错误的一件事:在Role类之外的函数中对角色代码进行硬编码。那是:

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

我想肯定是错的。我见过执行此操作的程序,然后由于某些人拼错了字符串而得到了神秘的错误。我记得有一次,当函数检查“ Stock”时,程序员写了“ stock”。

如果有100个不同的角色,我将很不愿意编写100个函数来检查每个可能的函数。您大概可以通过编写第一个函数,然后将其复制和粘贴99次来创建它们,以及您想打赌在那99个副本中有多少您会忘记更新测试,或者在下一次时下车您浏览了列表,所以现在有了

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类知道如何检查角色。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.