다음과 같은 경우 항상 필요한 최소 데이터를 함수에 전달해야합니다.


81

IsAdmin사용자가 관리자인지 확인 하는 기능 이 있다고 가정 해 봅시다 . 또한 관리자 확인은 사용자 ID, 이름 및 비밀번호를 일종의 규칙 (중요하지 않음)과 일치시켜 수행한다고 가정 해 보겠습니다.

내 머리에는 이것에 대한 두 가지 가능한 기능 서명이 있습니다.

public bool IsAdmin(User user);
public bool IsAdmin(int id, string name, string password);

가장 자주 두 번째 유형의 서명을 사용합니다.

  • 함수 서명은 독자에게 더 많은 정보를 제공합니다
  • 함수 안에 포함 된 로직은 User클래스에 대해 알 필요가 없습니다.
  • 일반적으로 함수 내부의 코드가 약간 적습니다.

그러나 때로는이 접근법에 의문을 제기하고 어느 시점에서 다루기가 어려워 질 것임을 알고 있습니다. 예를 들어 함수가 10 개의 서로 다른 객체 필드를 결과 bool에 매핑하는 경우 분명히 전체 객체를 보냅니다. 그러나 놀라운 예제와는 별도로 실제 객체를 전달할 이유를 알 수 없습니다.

나는 당신이 제공 할 수있는 일반적인 관찰뿐만 아니라 두 가지 스타일에 대한 논쟁에 감사한다.

나는 객체 지향 및 기능적 스타일로 프로그램하기 때문에 질문은 모든 관용구와 관련하여 보여야합니다.


40
주제 외 : 궁금한 점이 있는데 왜 사용자의 "관리자"상태가 비밀번호에 의존합니까?
stakx

43
@ syb0rg 잘못되었습니다. C #에서는 이것이 명명 규칙 입니다.
magnattic

68
@ syb0rg 맞습니다. 언어를 지정하지 않았기 때문에 특정 언어의 규칙을 위반한다고 말하는 것이 다소 이상하다고 생각합니다.
magnattic

31
@ syb0rg "함수 이름은 대문자로 시작해서는 안됩니다"라는 일반적인 설명은 잘못된 언어입니다. 어쨌든, 나는 당신을 화나게하려는 것이 아니 었습니다. 이것은 질문 자체에 대해 많은 논외가되고 있습니다. 나는 필요를 보지 못하지만 계속하려면 토론을 채팅으로 옮기십시오 .
magnattic

6
일반적으로 자신의 보안을 굴리는 것은 일반적으로 나쁜 일입니다 (tm) . 대부분의 언어와 프레임 워크에는 사용 가능한 보안 / 권한 시스템이 있으며 더 간단한 것을 원할 때도이를 사용해야하는 이유가 있습니다.
James Snell

답변:


123

개인적으로 첫 번째 방법을 선호합니다 IsAdmin(User user)

사용하기가 훨씬 쉽고 IsAdmin에 대한 기준이 나중에 (역할 또는 isActive를 기반으로) 나중에 변경되는 경우, 어디서나 메소드 서명을 다시 작성할 필요가 없습니다.

또한 사용자가 관리자인지 여부를 결정하는 속성을 광고하지 않거나 암호 속성을 모든 곳에서 전달하지 않기 때문에 아마 더 안전 할 것입니다. 그리고 syrion 은 당신 idname/ 와 일치하지 않을 때 어떤 일이 발생 하는지를 지적합니다 password.

함수 내부의 코드 길이는 실제로 메소드가 작업을 수행하는 데 중요하지 않아야하며 도우미 메소드 코드보다 짧고 간단한 응용 프로그램 코드가 훨씬 좋습니다.


3
리팩토링 문제는 매우 좋은 지적이라고 생각합니다. 감사합니다.
Anders Arpi

1
그렇지 않은 경우 메소드 서명 인수를 작성하려고했습니다. 이것은 다른 프로그래머가 유지 관리하는 메소드에 많은 의존성이있는 경우 특히 유용합니다. 이렇게하면 사람들이 서로의 길을 벗어날 수 있습니다. +1
jmort253

1
이 답변에 동의하지만 다른 접근 방식 ( "최소 데이터") 바람직한 두 가지 상황에 플래그를 지정하겠습니다 . (1) 메소드 결과를 캐시하려고합니다. 전체 객체를 캐시 키로 사용하지 않거나 매번 호출 할 때마다 객체의 속성을 확인해야합니다. (2) 메소드 / 함수가 비동기 적으로 실행되기를 원합니다. 인수를 직렬화하고 테이블에 저장하는 것이 필요할 수 있습니다. 보류중인 작업을 관리 할 때 오브젝트 블로 브보다 정수를 더 쉽고 간단하게 찾을 수 있습니다.
GladstoneKeep

기본 강박 관념 반 패턴은 단위 테스트에 끔찍할 수 있습니다.
사무엘

82

첫 번째 서명은 User객체 자체 에서 사용자 관련 로직을 캡슐화 할 수 있기 때문에 우수 합니다. 코드베이스에 뿌려진 "id, name, password"튜플의 구성에 대한 논리를 갖는 것은 유리하지 않습니다. 또한 isAdmin함수를 복잡하게 만듭니다. 예를 들어 누군가가에 id일치하지 않는를 전달하면 어떻게됩니까 name? 주어진 사용자가 자신의 암호를 모르는 상황에서 관리자인지 확인하려면 어떻게 해야 합니까?

또한 세 번째 요점에 대한 참고 사항으로 추가 인수가있는 스타일을 선호합니다. "함수 내에 코드가 적을"수 있지만 그 논리는 어디로 갑니까? 그냥 사라질 수 없습니다! 대신 함수가 호출되는 모든 단일 장소에 퍼져 있습니다. 한곳에 5 줄의 코드를 저장하기 위해 기능을 사용할 때마다 5 줄을 지불했습니다!


45
당신의 논리를 따르면, user.IsAdmin()훨씬 나아지지 않을까요?
Anders Arpi

13
네 그럼요.
asthasr

6
@ AndersHolmström 그것은 사용자가 일반적으로 관리자인지 또는 현재 페이지 / 양식의 관리자인지 테스트하는지 여부에 달려 있습니다. 일반적으로 그렇다면 더 좋을 것입니다. 그러나 IsAdmin특정 형식이나 기능 만 테스트 하는 경우 사용자와 관련이 없어야합니다
Rachel

40
@ 앤더스 : 아닙니다. 사용자가 관리자인지 여부를 결정해서는 안됩니다. 권한 또는 보안 시스템이 있어야합니다. 단단히 클래스에 결합되는 뭔가가 그것을 그 클래스의 일부로 만들기 위해 좋은 생각이다 의미하지 않는다
마르 얀 Venema의

11
@MarjanVenema-User 클래스가 인스턴스가 관리자인지 여부를 "결정"해서는 안된다는 아이디어는 나를 약간화물이 많은 것으로 생각합니다. 그 일반화를 그렇게 강력하게 만들 수는 없습니다. 요구 사항이 복잡하다면 별도의 "시스템"으로 캡슐화하는 것이 도움이되지만 KISS + YAGNI를 인용하여 결정을 내리는 것은 일반적인 규칙이 아니라 실제로 그 복잡성에 직면하고 싶습니다. 그리고주의 경우에도 논리가 사용자의 "일부"아닌 실용성의 문제는 사용자의 통과를 가지도록 여전히 유용 할 수있는 더 복잡한 시스템에 위양.
Eamon Nerbonne

65

다른 사람들이 주었던 이유 중 첫 번째 (단지)는 아닙니다.

public bool IsAdmin(User user);

안전한 타입입니다. 특히 User는 사용자가 직접 정의한 유형이므로 인수를 바꾸거나 전환 할 기회가 거의 없습니다. 그것은 분명히 사용자에서 작동하며 int와 두 개의 문자열을 허용하는 일반적인 기능이 아닙니다. 이것은 코드가 보이는 Java 또는 C #과 같은 형식 안전 언어를 사용하는 시점의 큰 부분입니다. 특정 언어에 대해 질문하는 경우 해당 언어의 태그를 질문에 추가 할 수 있습니다.

public bool IsAdmin(int id, string name, string password);

여기에 int와 2 개의 문자열이 있습니다. 이름과 비밀번호를 바꾸지 못하는 이유는 무엇입니까? 두 번째는 더 많은 작업을 사용하고 더 많은 오류 기회를 허용합니다.

아마도 두 함수 모두 함수 내에서 (첫 번째 경우) 또는 함수를 호출하기 전에 (두 번째 경우) user.getId (), user.getName () 및 user.getPassword ()를 호출해야합니다. 커플 링의 양은 어느 쪽이든 동일합니다. 실제로이 함수는 Users에서만 유효하므로 Java에서는 모든 인수를 제거하고 User 객체의 인스턴스 메소드로 만듭니다.

user.isAdmin();

이 기능은 이미 사용자와 밀접하게 연결되어 있으므로 사용자의 일부로 만드는 것이 좋습니다.

추신 : 이것은 단지 예일 것이라고 확신하지만 암호를 저장하는 것처럼 보입니다. 대신 암호로 안전한 암호 해시 만 저장해야합니다. 로그인하는 동안 제공된 비밀번호는 동일한 방식으로 해시되고 저장된 해시와 비교되어야합니다. 일반 텍스트로 암호를 보내지 않아야합니다. 이 규칙을 위반하고 isAdmin (int Str Str)이 사용자 이름을 기록한 경우 코드에 이름과 비밀번호를 바꾼 경우 대신 비밀번호가 기록되어 보안 문제가 발생합니다 (로그에 비밀번호를 쓰지 마십시오) ) 및 구성 요소 대신 객체를 전달하는 (또는 클래스 메소드를 사용하여) 선호하는 또 다른 주장입니다.


11
사용자가 관리자인지 여부를 결정해서는 안됩니다. 권한 또는 보안 시스템이 있어야합니다. 클래스에 밀접하게 연결된다고해서 해당 클래스의 일부로 만드는 것이 좋은 생각은 아닙니다.
Marjan Venema

6
@MarjanVenema 사용자가 아무것도 결정하지 않습니다. User 객체 / 테이블은 각 사용자에 대한 데이터를 저장합니다. 실제 사용자는 자신에 대한 모든 것을 변경할 수 없습니다. 사용자가 허용 된 것을 변경하더라도 이메일 주소 나 사용자 ID 또는 비밀번호를 변경하면 이메일과 같은 보안 경고가 트리거 될 수 있습니다. 사용자가 마술로 특정 객체 유형에 대한 모든 것을 변경할 수있는 시스템을 사용하고 있습니까? 나는 그런 시스템에 익숙하지 않다.
GlenPeterson

2
@GlenPeterson : 고의로 나를 오해하고 있습니까? 내가 사용자에게 말할 때, 물론 사용자 클래스를 의미했습니다.
Marjan Venema 2013

2
@GlenPeterson 주제를 완전히 벗어 났지만 여러 라운드의 해싱 및 암호화 기능은 데이터를 약화시킵니다.
Gusdor

3
@MarjanVenema : 고의적으로 어렵지는 않습니다. 나는 우리가 매우 다른 관점을 가지고 있다고 생각하며 당신의 관점을 더 잘 이해하고 싶습니다. 나는이 더 잘 설명 할 수있는 새로운 질문을 열었습니다 : programmers.stackexchange.com/questions/216443/...
GlenPeterson

6

선택한 언어가 값으로 구조를 전달하지 않으면 (User user)변형이 더 좋아 보입니다. 언젠가 사용자 이름을 삭제하고 ID / 암호를 사용하여 사용자를 식별하기로 결정한 경우 어떻게해야합니까?

또한이 접근법은 필연적으로 길고 지나친 메소드 호출 또는 불일치로 이어집니다. 3 개의 인수를 전달해도됩니까? 5 어때요? 아니면 6? 객체를 전달하는 것이 리소스 측면에서 저렴하다면 (아마도 3 개의 인수를 전달하는 것보다 저렴할 경우) 왜 그렇게 생각합니까?

그리고 두 번째 접근법은 독자에게 더 많은 정보를 제공한다는 데 동의하지 않습니다. 직관적으로 메소드 호출은 "사용자에게 관리자 권한이 있는지 여부"가 아니라 "주어진 ID / 이름 / 암호 조합에 관리자 권한이 있는지 여부"가 아닙니다.

따라서 User객체를 전달하여 큰 구조를 복사 하지 않으면 첫 번째 접근 방식이 더 깨끗하고 논리적입니다.


3

아마 둘 다 가지고있을 것입니다. 시간의 안정성에 대한 희망이있는 외부 API 인 첫 번째 API입니다. 두 번째는 외부 API에 의해 호출 된 개인 구현입니다.

나중에 검사 규칙을 변경해야하는 경우 외부 서명에서 호출 한 새 서명으로 새 개인 함수를 작성하면됩니다.

이는 내부 구현을 쉽게 변경할 수 있다는 장점이 있습니다. 또한 언젠가는 동시에 사용 가능한 두 기능이 필요할 수 있으며 외부 컨텍스트 변경에 따라 하나 또는 다른 함수를 호출하는 것이 일반적입니다 (예 : 다른 필드를 가진 기존 사용자와 새 사용자가 공존 할 수 있음).

질문의 제목과 관련하여 두 경우 모두 최소한의 필요한 정보를 제공합니다. 사용자는 정보를 포함하는 가장 작은 응용 프로그램 관련 데이터 구조 인 것 같습니다. id, password 및 name의 세 필드는 실제로 필요한 최소 구현 데이터 인 것 같지만 실제로는 응용 프로그램 수준 개체가 아닙니다.

즉, 데이터베이스를 다루는 경우 사용자는 레코드가되고 id, 암호 및 로그인은 해당 레코드의 필드가됩니다.


개인 방법의 이유를 알 수 없습니다. 왜 둘 다 유지합니까? 개인 메소드에 다른 인수가 필요한 경우 여전히 공개 인수를 수정해야합니다. 그리고 당신은 그것을 공개적으로 테스트 할 것입니다. 그리고 나중에 언젠가 두 가지가 모두 필요한 것은 아닙니다. 당신은 여기서 추측하고 있습니다-YAGNI.
Piotr Perak

예를 들어 사용자 데이터 구조에 처음부터 다른 필드가 없다고 가정합니다. 일반적으로 사실이 아닙니다. 두 번째 기능은 사용자의 내부 부분이 관리자인지 확인합니다. 일반적인 사용 사례는 종류가 될 수 있습니다. 사용자 생성 시간이 날짜 이전 인 경우 새 규칙을 호출하지 않으면 이전 규칙을 호출합니다 (사용자의 다른 부분에 따라 다를 수도 있고 다른 규칙을 사용하여 동일 할 수도 있음). 이 방법으로 코드를 분할하면 최소 개념 API와 최소 구현 함수라는 두 가지 최소 기능을 얻게됩니다.
kriss

다시 말해, 이런 방식으로 사용자의 어느 부분이 사용되는지의 여부를 내부 기능 시그니처에서 즉시 볼 수 있습니다. 프로덕션 코드에서는 항상 명확하지는 않습니다. 나는 현재 몇 년간의 유지 보수 이력을 가진 큰 코드베이스에서 일하고 있으며 이런 종류의 분리는 장기 유지 보수에 매우 유용합니다. 코드를 유지하지 않으려는 경우 실제로 쓸모가 없습니다 (그러나 안정적인 개념적 API를 제공하는 것조차도 쓸모가 없습니다).
kriss

다른 말로 : 외부 방법을 통해 내부 방법을 확실히 단위 테스트하지는 않습니다. 그것은 나쁜 시험 관행입니다.
kriss

1
이것은 좋은 습관이며 그 이름이 있습니다. 이것을 "단일 책임 원칙"이라고합니다. 클래스와 메소드 모두에 적용 할 수 있습니다. 단일 책임을 갖는 메소드를 갖는 것이 더 모듈화되고 유지 보수 가능한 코드를 작성하는 좋은 방법입니다. 이 경우 한 작업이 사용자 개체에서 기본 데이터 집합을 선택하고 다른 작업은 해당 기본 데이터 집합을 확인하여 사용자가 관리자인지 확인합니다. 이 원칙을 따르면 메소드를 작성하고 코드 중복을 피할 수 있다는 이점이 있습니다. 이것은 또한 @kriss가 언급했듯이 더 나은 단위 테스트를 받는다는 것을 의미합니다.
diegoreymendez

3

클래스 (참조 유형)를 메소드에 전송해야하는 단점은 Mass-Assignment Vulnerability 입니다. IsAdmin(User user)방법 대신에 방법이 있다고 상상해보십시오 UpdateUser(User user).

만약 User클래스라는 부울 속성을 가지고 IsAdmin, 나는 내 방법을 실행하는 동안 그것을 확인하지 않는 경우, 그때는 대량 할당에 취약 해요. GitHub는 2012 년에 바로이 서명으로 공격을 받았습니다.


2

나는이 접근법으로 갈 것입니다 :

public bool IsAdmin(this IUser user) { /* ... */ }

public class User: IUser { /* ... */ }

public interface IUser
{
    string Username {get;}
    string Password {get;}
    IID Id {get;}
}
  • 이 기능의 매개 변수로 인터페이스 유형을 사용하면 사용자 오브젝트를 전달하고 테스트를위한 모의 사용자 오브젝트를 정의 할 수 있으며이 기능의 사용 및 유지 보수에 대한 유연성이 향상됩니다.

  • 또한 this함수를 모든 IUser 클래스의 확장 메소드로 만들기 위해 키워드 를 추가했습니다 . 이 방법으로보다 친숙한 방식으로 코드를 작성하고 Linq 쿼리에서이 함수를 사용할 수 있습니다. 여전히이 기능을 IUser 및 User에서 정의하는 것이 더 간단하지만이 메소드를 해당 클래스 외부에두기로 결정한 이유가 있다고 가정합니다.

  • ID의 속성을 재정의 할 수 있기 때문에 IID대신 사용자 지정 인터페이스 를 사용합니다 int. 예를 들어 혹시에서 변경해야한다 intlong, Guid다른 사람, 또는 뭔가. 아마 과잉 일 수도 있지만, 초기 결정에 얽매이지 않도록 유연하게 만드는 것이 좋습니다.

  • 주의 : IUser 개체는 다른 네임 스페이스 및 / 또는 User 클래스의 어셈블리에있을 수 있으므로 사용자 코드를 하나의 참조 된 라이브러리 만 공유하여 IsAdmin 코드와 완전히 분리 할 수 ​​있습니다. 정확히 당신이 무엇을 할 수 있는지 의심이 항목을 사용하는 의심이 있습니다.


3
IUser는 여기서 쓸모가 없으며 복잡성을 추가합니다. 테스트에서 실제 사용자를 사용할 수없는 이유를 모르겠습니다.
Piotr Perak

1
적은 노력으로 미래의 유연성을 제공하므로 현재 가치를 추가하지 않으면 해가되지 않습니다. 개인적으로 나는 모든 시나리오에 대한 미래의 가능성을 생각할 필요가 없기 때문에 항상 이것을 선호합니다. 인터페이스에 고정).
JohnLBevan

인터페이스를 복용 당신은 그래서 당신은 간단한 테스트를 유지할 수 있습니다 그것을 조롱 할 수 있도록, 실제 사용자 클래스를 만들 복잡 할 수 있습니다 @Peri
JK합니다.

@JohnLBevan : 야 그니 !!!
Piotr Perak

@ jk. : 사용자를 구축하기 어렵다면 뭔가 잘못되었습니다
Piotr Perak

1

면책 조항 :

답장을 위해 스타일 문제에 집중하고 보안 관점에서 볼 때 ID, 로그인 및 일반 암호가 사용하기에 좋은 매개 변수 집합인지 잊어 버릴 것입니다. 사용자의 권한에 대해 알아 내기 위해 사용하는 기본 데이터 세트에 대한 대답을 추정 할 수 있어야합니다 ...

user.isAdmin()또한와 동등한 것으로 간주 합니다 IsAdmin(User user). 그것은 당신이 선택하는 것입니다.

댓글:

내 권장 사항은 다음 중 하나의 사용자에게만 해당됩니다.

public bool IsAdmin(User user);

또는 다음 라인을 따라 두 가지를 모두 사용하십시오.

public bool IsAdmin(User user); // calls the private method below
private bool IsAdmin(int id, string name, string password);

공개 방법의 이유 :

공개 메소드를 작성할 때 일반적으로 메소드 사용 방법에 대해 생각하는 것이 좋습니다.

이 특정한 경우, 일반적으로이 메소드를 호출하는 이유는 특정 사용자가 관리자인지 확인하기위한 것입니다. 그것이 당신이 필요로하는 방법입니다. 각 발신자가 올바른 매개 변수를 선택하여 전송할 필요가 없도록하고 코드 중복으로 인한 실수의 위험이 있습니다.

개인용 메소드의 이유 :

필요한 매개 변수의 최소 세트 만 수신하는 개인용 메소드가 좋은 경우가 있습니다. 때로는 그렇게 많지 않습니다.

이러한 메서드를 분할 할 때 좋은 점 중 하나는 동일한 클래스의 여러 공용 메서드에서 개인 버전을 호출 할 수 있다는 것입니다. (어떤 이유)에 대한 약간 다른 논리를 가지고 당신이 원하는 경우 예를 들어, Localuser그리고 RemoteUser(아마도 / 오프 원격 관리자 켜 설정있다?)

public bool IsAdmin(Localuser user); // Just call private method
public bool IsAdmin(RemoteUser user); // Check if setting is on, then call private method
private bool IsAdmin(int id, string name, string password);

또한 어떤 이유로 든 개인 메소드를 공개해야 할 경우 가능합니다. 로 변경 private하는 것만 큼 ​​쉽습니다 public. 이 경우에는 큰 이점처럼 보이지 않을 수도 있지만 때로는 실제로는 이점이 있습니다.

또한 단위 테스트를 수행하면 훨씬 더 많은 원자 테스트를 수행 할 수 있습니다. 일반적으로이 메소드에서 테스트를 실행하기 위해 전체 사용자 오브젝트를 작성할 필요가없는 것이 좋습니다. 그리고 전체 범위를 원하면 두 통화를 모두 테스트 할 수 있습니다.


0
public bool IsAdmin(int id, string name, string password);

나열된 이유로 나쁘다. 그것은 의미하지 않습니다

public bool IsAdmin(User user);

자동적으로 좋습니다. : 특히 사용자는 같은 방법을 갖는 객체 인 경우 getOrders(), getFriends(), getAdresses(), ...

id, name, password 만 포함하고 사용자가 필요없는 모든 사용자 데이터 대신 IsAdmin에 전달하는 UserCredentials 유형으로 도메인을 리팩토링하는 것을 고려할 수 있습니다.

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