문제

간단한 객체 지향 설계 문제에 어떻게 접근 할 것인지에 대해 질문하고 싶습니다. 나는이 시나리오를 다루는 가장 좋은 방법에 대한 몇 가지 아이디어를 가지고 있지만 스택 오버플로 커뮤니티의 의견을 듣는 데 관심이 있습니다. 관련 온라인 기사에 대한 링크도 감사합니다. C#을 사용하고 있지만 질문은 언어에 따라 다릅니다.

데이터베이스가있는 비디오 스토어 응용 프로그램을 작성한다고 가정 해 Person 테이블 PersonId, Name, DateOfBirth 그리고 Address 필드. 그것은 또한 a Staff a에 대한 링크가있는 테이블 PersonId, a Customer 링크 된 테이블 PersonId.

간단한 객체 지향적 접근 방식은 Customer "is a" Person 따라서 다음과 같은 수업을 만듭니다.

class Person {
    public int PersonId { get; set; }
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address { get; set; }
}

class Customer : Person {
    public int CustomerId { get; set; }
    public DateTime JoinedDate { get; set; }
}

class Staff : Person {
    public int StaffId { get; set; }
    public string JobTitle { get; set; }
}

이제 우리는 모든 고객에게 이메일을 보내는 기능을 작성할 수 있습니다.

static void SendEmailToCustomers(IEnumerable<Person> everyone) { 
    foreach(Person p in everyone)
        if(p is Customer)
            SendEmail(p);
}

이 시스템은 고객이자 직원 인 사람이있을 때까지 잘 작동합니다. 우리가 실제로 우리를 원하지 않는다고 가정합니다 everyone 같은 사람을 두 번, 한 번 Customer 그리고 한 번 Staff, 우리는 다음 사이에 임의적 인 선택을합니까?

class StaffCustomer : Customer { ...

그리고

class StaffCustomer : Staff { ...

분명히이 두 가지 중 첫 번째 만 SendEmailToCustomers 기능.

그래서 당신은 무엇을 하시겠습니까?

  • 만들기 Person 클래스는 a에 대한 선택적 참조를 가지고 있습니다 StaffDetails 그리고 CustomerDetails 수업?
  • 포함 된 새 클래스를 만듭니다 Person, 옵션 StaffDetails 그리고 CustomerDetails?
  • 모든 것을 인터페이스로 만드십시오 (예 : IPerson, IStaff, ICustomer) 그리고 적절한 인터페이스를 구현 한 세 가지 클래스를 만들 수 있습니까?
  • 완전히 완전히 다른 접근 방식을 취합니까?
도움이 되었습니까?

해결책

마크, 이것은 흥미로운 질문입니다. 당신은 이것에 대해 많은 의견을 찾을 것입니다. 나는 '올바른'대답이 있다고 생각하지 않습니다. 이것은 시스템을 구축 한 후 강성 상속 객체 설계가 실제로 문제를 일으킬 수있는 곳의 훌륭한 예입니다.

예를 들어, "고객"및 "직원"수업을 사용했다고 가정 해 봅시다. 당신은 당신의 시스템을 배포하고 모든 것이 행복합니다. 몇 주 후, 누군가는 자신이 '직원'과 '고객'이며 고객 이메일을받지 못하고 있다고 지적합니다. 이 경우 코드 변경 사항이 많이 있습니다 (재 디자인, 재 포인트).

나는 당신이 사람들과 그들의 역할의 모든 순열과 조합을 구현하는 파생 클래스 세트를 사용하려고 시도하면 지나치게 복잡하고 유지하기가 어렵다고 생각합니다. 위의 예는 매우 간단하다는 점을 감안할 때 특히 사실입니다. 대부분의 실제 응용 분야에서는 상황이 더 복잡 할 것입니다.

여기서 당신의 예를 들어, 나는 "완전히 완전히 다른 접근법을 취하십시오"와 함께 갈 것입니다. 나는 사람 클래스를 구현하고 "역할"모음을 포함시킬 것입니다. 각 사람은 "고객", "직원"및 "공급 업체"와 같은 하나 이상의 역할을 가질 수 있습니다.

따라서 새로운 요구 사항이 발견 될 때 역할을 더 쉽게 추가 할 수 있습니다. 예를 들어, 단순히 "역할"클래스를 가질 수 있으며 새로운 역할을 도출 할 수 있습니다.

다른 팁

사용을 고려할 수 있습니다 파티 및 책임 패턴

이런 식으로 사람은 고객 또는 직원 일 수있는 책임 모음을 갖게됩니다.

나중에 더 많은 관계 유형을 추가하면 모델도 더 간단합니다.

순수한 접근법은 다음과 같습니다. 모든 것을 인터페이스로 만드십시오. 구현 세부 사항은 선택적으로 다양한 형태의 구성 또는 구현 이점을 사용할 수 있습니다. 이것들은 구현 세부 사항이므로 공개 API에 중요하지 않으므로 인생을 가장 간단하게 만드는 사람을 자유롭게 선택할 수 있습니다.

사람은 인간이지만 고객은 사람이 때때로 채택 할 수있는 역할 일뿐입니다. 남자와 여자는 사람을 물려받는 후보자가 될 것이지만 고객은 다른 개념입니다.

Liskov 대체 원칙에 따르면 우리는 기본 클래스에 대한 언급이있는 곳에서 파생 된 클래스를 사용할 수 있어야한다고 말합니다. 고객이 상속하는 사람이 있으면 이것을 위반할 수 있습니다. 고객은 아마도 조직의 역할 일 수도 있습니다.

Foredecker의 답변을 올바르게 이해하면 알려주세요. 여기 내 코드가 있습니다 (파이썬으로; 죄송합니다. C#을 모릅니다). 유일한 차이점은 사람이 "고객"이라면 무언가를 알리지 않는다는 것입니다. 그의 역할 중 하나가 "그 일에 관심이 있다면"그렇게 할 것입니다. 이것은 충분히 유연합니까?

# --------- PERSON ----------------

class Person:
    def __init__(self, personId, name, dateOfBirth, address):
        self.personId = personId
        self.name = name
        self.dateOfBirth = dateOfBirth
        self.address = address
        self.roles = []

    def addRole(self, role):
        self.roles.append(role)

    def interestedIn(self, subject):
        for role in self.roles:
            if role.interestedIn(subject):
                return True
        return False

    def sendEmail(self, email):
        # send the email
        print "Sent email to", self.name

# --------- ROLE ----------------

NEW_DVDS = 1
NEW_SCHEDULE = 2

class Role:
    def __init__(self):
        self.interests = []

    def interestedIn(self, subject):
        return subject in self.interests

class CustomerRole(Role):
    def __init__(self, customerId, joinedDate):
        self.customerId = customerId
        self.joinedDate = joinedDate
        self.interests.append(NEW_DVDS)

class StaffRole(Role):
    def __init__(self, staffId, jobTitle):
        self.staffId = staffId
        self.jobTitle = jobTitle
        self.interests.append(NEW_SCHEDULE)

# --------- NOTIFY STUFF ----------------

def notifyNewDVDs(emailWithTitles):
    for person in persons:
        if person.interestedIn(NEW_DVDS):
            person.sendEmail(emailWithTitles)

나는 "is"check ( "instancef"in java)를 피할 것입니다. 한 가지 해결책은 a를 사용하는 것입니다 데코레이터 패턴. 이메일 가능한 사람이 구성을 사용하여 개인의 개인 인스턴스를 보유하고 모든 비자마 방법을 사람의 대상에 위임하는 사람을 장식하는 이메일 가능한 사람을 만들 수 있습니다.

우리는 작년에 대학 에서이 문제를 연구했으며, 에펠을 배우고 있었기 때문에 여러 상속을 사용했습니다. 어쨌든 Foredecker 역할 대안은 충분히 유연한 것 같습니다.

직원 인 고객에게 이메일을 보내는 데 무엇이 잘못 되었습니까? 그가 고객이라면 이메일을 보낼 수 있습니다. 내가 그렇게 생각하는 데 틀렸어? 그리고 왜 "모두"를 이메일 목록으로 받아 들여야합니까? Woudlnt "SendeMailtoveryOne"방법이 아닌 "SendEmailTocustomer"방법을 다루고 있기 때문에 고객 목록을 갖는 것이 좋습니다. "모두"목록을 사용하려면 해당 목록에서 복제를 허용 할 수 없습니다.

이들 중 어느 것도 많은 Redisgn으로 달성 할 수 없다면, 나는 첫 번째 Foredecker 답변과 함께 갈 것이며 각 사람에게 할당 된 역할이 있어야 할 수도 있습니다.

귀하의 클래스는 단지 데이터 구조 일뿐입니다. 그중 어느 것도 행동이없고, 그냥 getters and setter 만 있습니다. 상속은 여기에 부적절합니다.

다른 완전히 다른 접근법을 취하십시오. 직원 직원의 문제는 직원 구성원이 직원으로 시작하여 나중에 고객이 될 수 있으므로 직원으로 삭제하고 직원 수업의 새로운 사례를 만들어야한다는 것입니다. 아마도 'iscustomer'의 직원 수업 내에서 간단한 부울은 우리 모두가 이미 고객으로 포함되어 있음을 알기 때문에 모든 사람이 목록 (모든 고객과 모든 직원과 모든 직원을 적절한 테이블로부터 고집함으로써 컴파일)을 허용 할 수 있습니다.

다음은 몇 가지 팁입니다.“이것을 생각조차하지 마라”의 범주에서 여기에 발생하는 코드의 나쁜 예는 다음과 같습니다.

파인더 메소드는 객체를 반환합니다

문제 : 발생 수에 따라 파인더 메소드가 발생 수를 나타내는 숫자를 반환합니다. 하나만 발견하면 실제 객체를 반환합니다.

이것을하지 마십시오! 이것은 최악의 코딩 관행 중 하나이며 모호성을 소개하고 다른 개발자가 놀러 가면 그녀 가이 일을하는 것에 대해 당신을 미워할 것입니다.

솔루션 : 이러한 2 가지 기능이 필요하다면 : 인스턴스 계산 및 가져 오면 인스턴스를 반환하는 2 가지 방법과 인스턴스를 반환하는 방법을 만들지 만 두 가지 방법을 모두 수행하지 않습니다.

문제 : 파생 된 나쁜 관행은 Finder 메소드가 하나의 단일 발생을 반환 할 때, 하나 이상의 발견 된 경우 발생하는 배열을 발견 할 때입니다. 이 게으른 프로그래밍 스타일은 일반적으로 이전의 프로그래머에 의해 많이 이루어집니다.

솔루션 : 이것을 내 손에 넣으면 길이 1 (1)의 배열이 하나만 발견되면 길이가> 1 인 배열이 더 많이 발견되면 반환합니다. 또한, 전혀 발생하지 않으면 응용 프로그램에 따라 NULL 또는 길이 0의 배열이 반환됩니다.

인터페이스로 프로그래밍하고 공분산 반환 유형을 사용합니다

문제 : 인터페이스로 프로그래밍하고 공분산 반환 유형을 사용하고 호출 코드에 캐스팅.

솔루션 : 반환 된 값을 가리켜야하는 변수를 정의하기 위해 인터페이스에 정의 된 동일한 슈퍼 타입을 대신 사용하십시오. 이것은 프로그래밍을 인터페이스 접근 방식과 코드를 깨끗하게 유지합니다.

1000 줄 이상의 클래스는 100 줄 이상의 라인이 숨어있는 위험한 방법으로 숨어있는 위험입니다!

문제 : 일부 개발자는 한 클래스/방법으로 너무 많은 기능을 수행하여 기능을 중단하기에는 너무 게으르기 때문에 응집력이 낮고 커플 링이 높아집니다. OOP의 매우 중요한 원칙의 역수입니다! 솔루션 : 너무 많은 내부/중첩 클래스를 사용하지 마십시오.이 클래스는 필요에 따라 만 사용해야합니다. 그것들을 사용하면 상속 제한과 같은 더 많은 문제가 발생할 수 있습니다. 코드 중복 코드를 찾고 있습니다! 동일하거나 너무 유사한 코드는 이미 일부 슈퍼 타입 구현 또는 다른 클래스에 존재할 수 있습니다. 슈퍼 형이 아닌 다른 클래스에있는 경우 응집 규칙을 위반했습니다. 정적 방법을 조심하십시오 - 아마도 추가 할 유틸리티 클래스가 필요할 것입니다!
더보기 :http://centraladvisor.com/it/oop-what-are-the-best-practices-in-op

당신은 아마 이것에 상속을 사용하고 싶지 않을 것입니다. 대신 시도해보십시오.

class Person {
    public int PersonId { get; set; }
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address { get; set; }
}

class Customer{
    public Person PersonInfo;
    public int CustomerId { get; set; }
    public DateTime JoinedDate { get; set; }
}

class Staff {
    public Person PersonInfo;
    public int StaffId { get; set; }
    public string JobTitle { get; set; }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top