2014-06-22 119 views
2

桑迪屬於Person類,他轉換爲穆斯林並擁有一個新名字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,6Person

瑪麗最好的朋友是桑迪。

法蒂瑪= 0x23fec0,6Muslim

瑪麗最好的朋友是桑迪。

當然,我們希望有:瑪麗的最好的朋友是法蒂瑪,與瑪麗 - > getBestFriend() - > getReligion()==伊斯蘭教等... 如何重新設計整個的事情,所以,這是自動化(假設有成千上萬的人認識她)?

我想使用繼承,因爲類穆斯林將有許多複雜的人類方法(以及許多新的數據成員和方法)覆蓋。

+0

繼承似乎在這裏矯枉過正。爲什麼不只有'人'的'宗教'成員? –

+0

@約瑟夫曼斯菲爾德。因爲穆斯林類對Person方法有很多複雜的覆蓋。 – prestokeys

+0

@prestokeys在這種情況下,爲什麼不使用依賴注入來執行這些操作並注入一個基於宗教的不同集合(假設這是您的相關性的工作原理) – DNT

回答

1

我的第一種方法是,如@Joseph Mansfield在評論中所建議的,使religion的屬性爲Person。因此,這種改變只會意味着屬性的變化,而不是新的對象。儘管如此,如果你有一個不同的類是強制性的,你可以使用觀察者模式。也就是說,任何人的朋友都應該訂閱(在朋友地址簿中)列表中的人物變化。這個用戶必須從同一個接口(用戶)實現一個方法,當它的屬性發生變化時,它應該被Person對象調用。

綜上所述:

  • 有在Personfriends)每個朋友的地址列表。
  • 每個朋友的對象類應該實現其中一個更改通知方法聲明一個公共接口(friendChanged(...)
  • Person變化通過創建新類的一個新對象,它遍歷其friends名單打電話給friendChanged和傳球他們是新的人物對象地址。之後,您可以銷燬和釋放舊對象。

最後,我不認爲宗教應該表示爲一個子類。宗教對我來說是一種人的特徵,因此它應該是一種屬性。有一個OOP原則說:Favor composition over inheritance。你的設計似乎是人爲地違背這個原則。

+0

這看起來像一個好的開始。但是假設類籃球隊作爲一名隊員擁有沙灘。因此,當桑迪變成一個新班級時,BasketBallTeam應該有相同類型的觀察員模式訂閱來照顧她的地址變更?從本質上講,具有指針數據成員的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