2014-06-22 3 views
2

샌디는 무슬림으로 전환하여 파티마라는 새로운 이름을 사용하고 새로운 무슬림 속성 (예 : 종교 == 이슬람교 등)을 가진 Sandy의 모든 속성을 가진 Person 클래스입니다. . 이 시점에서 샌디는 삭제 될 수 있으며, 현재 이슬람 교실의 파티마 (Fatima)는 샌디 (Sandy)의 역할을 수행 할 것입니다. 문제는 새 주소로 인해 샌디가 파티마를 알지 못한다는 것을 아는 모든 사람들입니다. Sandy의 주소를 Fatima의 주소로 수동으로 변경하면 Sandy를 아는 모든 사람들이 분명히 받아 들일 수있는 방법이 아닙니다. 디자인을 개선하는 방법에 대한 제안?오브젝트를 다른 클래스로 변환

#include <iostream> 
#include <string> 
#include <typeinfo> 

class Person { 
     std::string name; 
     Person* bestFriend; 
    public: 
     Person (const std::string& newName) : name (newName) {} 
     virtual ~Person() = default; 
     std::string getName() const {return name;} 
     void setName (const std::string& newName) {name = newName;} 
     Person* getBestFriend() const {return bestFriend;} 
     void setBestFriend (Person* newBestFriend) {bestFriend = newBestFriend;} 
}; 

class Muslim: public Person { 
    public: 
     Muslim (const Person& person) : Person (person) { 
          // religion = Islam; etc... 
       } 
}; 

int main() { 
    Person *mary = new Person ("Mary"), *sandy = new Person ("Sandy"); 
    mary->setBestFriend (sandy); 
    std::cout << "sandy = " << sandy << ", " << typeid(*sandy).name() << std::endl; 
    std::cout << mary->getName() << "'s best friend is " << mary->getBestFriend()-> 
    getName() << "." << std::endl; 
    Muslim* fatima = new Muslim (static_cast<Muslim>(*sandy)); // the big change 
    fatima->setName ("Fatima"); // should now delete sandy, because fatima takes on every attribue of sandy 
    std::cout << "fatima = " << fatima << ", " << typeid(*fatima).name() << std::endl; 
    std::cout << mary->getName() << "'s best friend is " << mary->getBestFriend()-> 
    getName() << "." << std::endl; // still Sandy, of course 
} 

출력 :

모래 = 0x32658, 6 인

메리의 가장 친한 친구 샌디 여기에 문제를 보여주는 내 간단한 코드입니다. 0x23fec0

파티마 = 6Muslim

메리의 가장 친한 친구 샌디이다.

물론, 우리는 다음을 원합니다 : Mary의 가장 친한 친구는 mary-> getBestFriend() -> getReligion() == 이슬람교 등과 같은 파티마입니다. 모든 것을 재 설계하여 자동화되도록하는 방법 (그녀를 아는 사람들이 수천 명이 있다고 가정)?

클래스 무슬림은 Person 메소드 (및 많은 새로운 데이터 멤버 및 메소드)에 대해 여러 가지 복잡한 우선 순위를 갖기 때문에 상속을 사용하려고합니다.

+0

상속은 여기에서 과도한 것처럼 보입니다. 왜 '사람'이라는 종교 회원 만있는 것이 아닌가? –

+0

@Joseph Mansfield. 왜냐하면 클래스 무슬림은 Person 메소드에 대해 여러 가지 복잡한 우선 순위를 갖기 때문입니다. – prestokeys

+0

@prestokeys 이런 경우에, 의존성 주사를 사용하여 종교에 기초한 다른 세트를 주입하지 마십시오 (상관 관계가 어떻게 작용하는지 가정) – DNT

답변

1

내 첫 번째 접근 방식은 @Joseph Mansfield가 의견에서 제안한대로 religion의 속성을 Person으로 만드는 것입니다. 따라서 변경 내용은 새 개체가 아니라 특성 변경을 의미합니다.

그럼에도 불구하고 다른 클래스가 필수 인 경우 관찰자 패턴을 사용할 수 있습니다. 즉, 사람의 모든 친구는 친구 주소록에 등록 된 사람의 목록을 변경해야합니다. 이 구독자는 속성이 변경 될 때 Person 개체가 호출해야하는 동일한 인터페이스 (구독자)의 메서드를 구현해야합니다.

요약하면 :

  • Person (friends) 각 친구에게 주소 목록을 되세요.
  • 각 친구 객체 클래스는 변경 알림 방법을 선언 공통 인터페이스를 구현해야합니다 (friendChanged(...))
  • 새 클래스의 새로운 객체를 생성하여 Person 변경, 그것은 friendChanged 및 전달에 호출의 friends 목록을 통과해야 할 때 그들에게 새로운 사람의 개체 주소. 그런 다음 이전 개체를 삭제하고 할당을 취소 할 수 있습니다.

마지막으로 나는 종교가 하위 클래스로 표현되어야한다고 생각하지 않습니다. 종교는 사람의 특징으로 내게 들리는 것이므로 속성이어야합니다. OOP 원리는 다음과 같습니다 : Favor composition over inheritance. 당신은 그 원칙을 인위적으로 생각하는 듯합니다.

+0

이것은 좋은 시작 같습니다. 그러나 BasketBallTeam 클래스가 sandy를 멤버로 가졌다 고 가정합니다. 그래서 BasketBallTeam은 Sandy가 새로운 클래스로 바뀌면 주소가 바뀌는 것을 관찰하기 위해 같은 유형의 관찰자 패턴 구독을해야합니까? 본질적으로 Person에게 포인터 데이터 멤버가있는 클래스도 마찬가지로해야합니까? 나는 그것이 모든 것을 작동하게한다면 그것을 실행하는 것을 좋아한다 (전혀 문제가되지 않는다). – prestokeys

+0

@prestokeys BasketBallTeam이 관찰자 인터페이스 (추상 클래스)를 구현하도록 만들 수 있습니다. BasketBallTeam 관찰자 구현을 각 BasketBallTeam 팀 멤버 인 'friendChanged'메소드에 위임 할 수도 있습니다. –

+2

이것은 좋은 해결책입니다. 다중 스레드 프로그램에서 구현할 때 매우 정확한 수정이 필요합니다.이 프로그램에서는 상호적인 친구들의 동시 변경이 발생할 수 있습니다. –

0
class Person; 

class ObserverPersonInterface { 
    public: 
     virtual void registerObserver (Person*) = 0; 
     virtual void removeObserver (Person*) = 0; 
     virtual void notifyObservers() const = 0;  
}; 

class AddressChangeData : public ObserverPersonInterface { 
    public: 
     void addressChange (const Person* oldPerson, Person* newPerson) { 
      oldAddress = oldPerson; 
      newAddress = newPerson; 
      // observers.remove (oldAddress); // but not relevant here 
      notifyObservers(); 
     } 
     virtual void registerObserver(Person* person) override { 
      observers.emplace_back (person); 
     } 
     virtual void removeObserver(Person* person) override { 
      observers.remove (person); 
     } 
    private: 
     virtual inline void notifyObservers() const override; 

     std::list<Person*> observers; // should perhaps be a red-black tree for greater efficiency 
     const Person *oldAddress; 
     Person *newAddress; 
} changeOfAddressData; 

class Person { 
    public: 
     Person() { 
      changeOfAddressData.registerObserver(this); 
     } 
     // every new Person constructed is registered to changeOfAddressData 
     Person(const std::string& newName) : name (newName) { 
      changeOfAddressData.registerObserver (this); 
     } 
     virtual ~Person() = default; 
     std::string getName() const {return name;} 
     void setName (const std::string& newName) {name = newName;} 
     Person* getBestFriend() const {return bestFriend;} 
     void setBestFriend (Person* newBestFriend) {bestFriend = newBestFriend;} 
    protected: 
     void notifyChangeOfAddress(const Person* oldAddress, Person* newAddress) const { 
      changeOfAddressData.addressChange (oldAddress, newAddress); 
      delete oldAddress; 
     } 
    private: 
     std::string name; 
     Person* bestFriend; 

}; 

class Muslim : public Person { 
    public: 
     Muslim() = default; 
     Muslim(const Person& person) : Person(person) { 
      changeOfAddressData.registerObserver (this); 
      notifyChangeOfAddress (&person, this); 
     } 
}; 

inline void AddressChangeData::notifyObservers() const { 
    for (Person* x : observers) { 
     if(x->getBestFriend() == oldAddress) { 
      x->setBestFriend(newAddress); 
     } 
    } 
} 

int main() { 
    Person *mary = new Person ("Mary"), *sandy = new Person ("Sandy"); 

    mary->setBestFriend (sandy); 
    sandy->setBestFriend (mary); 

    std::cout << "mary = " << mary << ", " << typeid(*mary).name() << std::endl; 
    std::cout << "sandy = " << sandy << ", " << typeid(*sandy).name() << std::endl; 
    std::cout << mary->getName() << "'s best friend is " << mary->getBestFriend()->getName() << "." << std::endl; 
    std::cout << sandy->getName() << "'s best friend is " << sandy->getBestFriend()->getName() << "." << std::endl; 

    Muslim* fatima = new Muslim(static_cast<Muslim>(*sandy)); 

    // all subscribers of changeOfAddressData notified of sandy's address change, sandy is deleted automatically 
    fatima->setName("Fatima"); 

    std::cout << "fatima = " << fatima << ", " << typeid(*fatima).name() << std::endl; 
    std::cout << mary->getName() << "'s best friend is " << mary->getBestFriend()->getName() << "." << std::endl; 
    std::cout << fatima->getName() << "'s best friend is " << fatima->getBestFriend()->getName() << "." << std::endl; 

    Muslim* shazia = new Muslim (static_cast<Muslim>(*mary)); 
    // all subscribers of changeOfAddressData notified of mary's address change, mary is deleted automatically 
    shazia->setName ("Shazia"); 

    std::cout << "shazia = " << shazia << ", " << typeid(*shazia).name() << std::endl; 
    std::cout << shazia->getName() << "'s best friend is " << shazia->getBestFriend()->getName() << "." << std::endl; 
    std::cout << fatima->getName() << "'s best friend is " << fatima->getBestFriend()->getName() << "." << std::endl; 

    std::cin.get(); 
} 
+0

여기에 게시 할 코드를 작성할 때는 IDE 탭 설정을 4 칸으로 설정하고 탭을 공백으로 바꾸십시오. 이렇게하면 단순히 코드를 그대로 복사하여 붙여 넣고 강조 표시 한 다음 코드 블록 단추를 클릭하면 입력 한대로 보입니다. 탭을 공백으로 바꾸면 코드를 보는 다른 사람이 다른 탭 설정을 사용하는 경우 코드가 이해하기 어려운 것처럼 보이지 않게됩니다. – Casey

+0

이것은 답변이 아니며 단지 코드의 전부입니다. 컨텍스트를 추가하십시오. – Shoe

+0

@ Jeffrey. 코드를 실행하면 원래 작업이 올바르게 수행됩니다. 설명은 Pablo Hidalgo가 이미 제시했는데, 그는 그것을 제안했다. – prestokeys

관련 문제