게터를 사용하여 상수를 피하는 것이 좋은 방법입니까?


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).
코드 InChaos


4
묻지 말 것 원칙을 확인하십시오 .
Andy

전제에 의문을 제기합니다. User.getRole().getCode()코드를 역할과 비교하면 코드가 더 불쾌하게됩니다.
msw

답변:


47

우선, Demeter의 법칙에entity.underlyingEntity.underlyingEntity.method() 따라 비슷한 일을하는 것은 코드 냄새로 간주됩니다 . 이런 식으로 소비자에게 많은 구현 세부 사항을 노출시킵니다. 그리고 그러한 시스템을 확장하거나 수정해야 할 때마다 많은 피해를 입을 것입니다.

따라서 코드 인 카오스의 의견 에 따라 HasRole또는 IsAdmin방법을 사용 하는 것이 좋습니다 User. 이러한 방식으로 사용자에게 역할이 구현되는 방식은 소비자에 대한 구현 세부 사항으로 남아 있습니다. 또한 자신의 역할에 대한 세부 정보를 요청한 다음 그에 따라 결정하는 대신 사용자에게 자신의 역할이 무엇인지 묻는 것이 더 자연스러운 느낌입니다.


string필요한 경우가 아니면 s를 사용 하지 마십시오 . 내용을 미리 알 수 없기 때문에 변수 name의 좋은 예입니다 string. 반면 role에 컴파일 타임에 잘 알려진 두 개의 고유 한 값이있는 경우 강력한 타이핑을 사용하는 것이 좋습니다. 열거 형이 작동하는 곳입니다 ...

비교

public bool HasRole(string role)

public enum Role { Admin, User }

public bool HasRole(Role role)

두 번째 경우는 내가 무엇을 전달해야하는지에 대한 더 많은 아이디어를 제공합니다. 그것은 또한 string당신의 역할 상수에 대해 전혀 몰랐을 때 잘못으로 잘못 전달하는 것을 막습니다 .


다음은 역할이 어떻게 보일지에 대한 결정입니다. 사용자에게 직접 저장된 enum을 사용할 수 있습니다.

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
이것이 사람들이 모든 곳에서하는 방식 이었으면 좋겠습니다. 다른 것과 동등한 지 여부를 확인하기 위해 privatr 속성에 게터를 갖는 것은 그렇게 끔찍한 관행입니다.
Andy

2
"instance.property.property.method () ..."다시 유체 가 아닌가 ?
Peter Mortensen

2
@PeterMortensen 유창한 인터페이스와 다릅니다. 유창한 인터페이스 유형 X은 확실히 같은 함수 호출 문자열을 만들 수 있습니다 X.foo().bar().fee().... 이 유창한 인터페이스에서 foo, bar 및 fee는 모두 Xtype 객체를 반환하는 클래스 내부의 함수 X입니다. 그러나이 예제에서 언급 한 'instance.property.property.method ()의 경우 두 property호출은 실제로 별도의 클래스에 있습니다. 문제는 낮은 수준의 세부 정보를 얻기 위해 여러 계층의 추상화를 통과한다는 것입니다.
Shaz

10
좋은 대답이지만 데메테르 법칙은 도트 카운팅 연습이 아닙니다. instance.property.property.method()반드시 위반 또는 코드 냄새가 아닙니다. 객체 또는 데이터 구조로 작업하고 있는지 여부에 따라 다릅니다. Node.Parent.RightChild.Remove()아마 LoD를 위반하지 않을 것입니다 (다른 이유로 냄새가 나더라도). var role = User.Role; var roleCode = role.Code; var isAdmin = roleCode == ADMIN;"항상 한 점만 사용했다"는 사실에도 불구하고 거의 확실하게 위반입니다.
Carl Leth

1
나는 그것이 instance.property.property.method()LoD에 위배 된다는 것을 이해 하지만 OP는 instance.method().method()괜찮을 것입니다. 마지막 예제에서 상용구 코드가 너무 많아서 User의 외관 만 제공합니다 Role.
Bergi

9

저장된 코드가 관리자 코드인지 여부를 확인하는 기능이있는 것은 멍청한 것 같습니다. 당신이 정말로 알고 싶은 것은 그 사람이 관리자인지 여부입니다. 그래서 경우에 당신이 상수를 노출하고 싶지 않아, 당신은 또한 코드가 있음을 노출해서는 안되며, 방법의 isAdmin ()와 isUser ()를 호출합니다.

"User.getRole (). getCode () == Role.CODE_ADMIN 인 경우"는 실제로 사용자가 관리자인지 확인하는 데 도움이됩니다. 개발자가 그 라인을 작성하는 것을 기억해야하는 것은 무엇입니까? 사용자에게는 역할이 있고, 역할에는 코드가 있으며, 역할 클래스에는 코드에 대한 상수가 있음을 기억해야합니다. 그것은 순전히 구현에 관한 많은 정보입니다.


3
더 나쁜 것은 : 사용자가 항상 하나의 역할을 가지고 있다는 것입니다.
중복 제거기

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 클래스에 역할을 확인하는 방법을 알려주십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.