#include <iostream> #include <typeinfo> // typeid 를 사용하기 위해서 using namespace std; // 문제 : 디바이스 타입별로 읽기, 쓰기가 다르고 앞으로도 계속 새로운 디바이스 타입들이 나올텐데 어떻게 하면 기존 프로그램의 수정을 최소로 하고 기계적으로 // 새로운 디바이스를 지원할 수 있을까? // 해결책 : 디바이스 타입별로 다른 읽기/쓰기를 독립된 각각 독립된 객체로 만들고 이 것을 이용하는 client에 이 객체들을 담을 수 있는 추상클래스를 갖도록 하고 // 전체 디바이스를 아우르는 Device 추상 클래스를 만들고 구체적인 디바이스 타입별 클래스는 이 녀석을 상속한다. Client는 추상클래스(인터페이스)에 대해 프로그래밍한다. // Client는 추상클래스에 대해 읽기/쓰기를 하기 전에 추상클래스 변수에 실제 디바이스별 읽기/쓰기 // 객체가 들어가 있도록 해야한다. 즉, 전략패턴을 사용한다. 여기서 전략은 디바이스 타입별로 적용하는 "읽기"와 "쓰기"이다. // 전략의 유연성이 Device 추상클래스내 읽기, 쓰기 추상클래스가 주는 polymorphism을 축으로 갖추어졌다. // ----------------------- CReadAbstract 추상 클래스 : C++ 에서는 java와 달리 interface가 없어 abstract class로 만듬 ---------------------------------- class CReadAbstract // 디바이스를 읽는 것을 전략 패턴을 써 만든다. 나중에 이 변수 타입에 디바이스 타입별 실제 "디바이스 읽기 객체(전략)"가 대응된다. { public: virtual void read() = 0; // CReadAbstract 는 Abstract class because of this pure virtual function virtual ~CReadAbstract() { cout << "In CReadAbstract destructor!" << endl; } // 기타 메소드들 private: // 기타 attribute과 메소드들 }; class CRead_Android_SSGalaxy3 : public CReadAbstract // 실제로 Android_SSGalaxy3 를 읽는 객체. { public: void read() // CReadAbstract에서 파생된 실제 객체이기에 CReadAbstract에 규정된 read()를 Android_SSGalaxy3에 맞게 구현해야 함. { cout << "I am reading Android_SSGalaxy3" << endl; // 간단한 예 } // 기타 CRead_Android_SSGalaxy3 에 필요한 attribute과 메소드들 }; class CRead_iPhone4GS : public CReadAbstract // 실제로 iPhone4GS 를 읽는 객체. { public: void read() { cout << "I am reading iPhone4GS " << endl; } // 기타 attribute과 메소드들 }; // ----------------------- CReadAbstract 추상 클래스와 파생 concrete 클래스 종료 ------------------------- // ----------------------- CWriteAbstract 추상 클래스 : ------------------------- class CWriteAbstract { public: virtual void write() = 0; // make this class to Abstract class by this pure virtual function virtual ~CWriteAbstract() { cout << "In CWriteAbstract destructor!" << endl; } // 기타 attribute과 메소드들 }; class CWrite_Android_SSGalaxy3 : public CWriteAbstract { public: void write() { cout << "I am writing Android_SSGalaxy3" << endl; } // 기타 attribute과 메소드들 }; class CWrite_iPhone4GS : public CWriteAbstract { public: void write() { cout << "I am writing iPhone4GS" << endl; } // 기타 attribute과 메소드들 }; // ----------------------- End of CWriteAbstract 추상 클래스 & child classes ------------------------- // ----------------------- Device class. Device 클래스는 추상 클래스 ------------------------- class CDevice { public: enum DeviceType {Android_SSGalaxy3, iPhone4GS, LG_F860}; // 나중에 활용... CReadAbstract *readType ; // CDevice 는 내부에 CReadAbstract 타입의 멤버 변수를 갖고 있음 CWriteAbstract *writeType ; virtual void getDeviceType() = 0; // Make CDevice an abstract class by having a pure virtual function void readDevice() { readType->read(); // CDevice 에서 readDevice()를 호출하면 실제로 이것으로 위임(Delegate). readType에 이미 객체가 대응되어 있어야 함. } void setReadDevice(CReadAbstract *device_Read) { cout << "읽어야 할 디바이스를 변경..." << endl; delete readType; // Delete existing readTemplate before assigning new device instance readType = device_Read; } void writeDevice() { writeType->write(); } void setWriteDevice(CWriteAbstract *device_Write) { cout << "Write 할 디바이스를 변경......" << endl; delete writeType; writeType = device_Write; } // Provide a destructor to free the memory used by this behaviours virtual ~CDevice() { cout << endl << "In Device destructor deleting read and write template behaviours..." << endl; delete readType; delete writeType; } private: DeviceType thisDeviceType; // put attributes & methods that seem to fit. }; // ------------------------ make this a concrete class derived from CDevice interface, I mean abstract class ------------------------------ class CAndroid_SSGalaxy3 : public CDevice // CDevice is an abstract class { public: CAndroid_SSGalaxy3() { readType = new CRead_Android_SSGalaxy3(); // Android_SSGalaxy3 를 읽기 위해 필요한 구체적인 CRead_Android_SSGalaxy3 객체(전략)을 대응시킴 writeType = new CWrite_Android_SSGalaxy3(); // Android_SSGalaxy3에 쓰기 위해 필요한 구체적인 CWrite_Android_SSGalaxy3 객체(전략)을 대응시킴 } void getDeviceType() // got to implement this { cout << "나의 정체는 Android_SSGalaxy3" << endl; } void testfn() { test(); } // 기타 attribute과 메소드들 private: void test() { cout << "RTTI/Reflection test in Android_SSGalaxy3. Use real reflection library, man. " << endl ; } }; class CiPhone4GS : public CDevice { public: CiPhone4GS() { readType = new CRead_iPhone4GS(); writeType = new CWrite_iPhone4GS(); } void getDeviceType() { cout << "I am iPhone4GS, really" << endl; } // 기타 attribute과 메소드들 }; // ----------------------- End of Device subtypes ------------------------- // 만약에 이 상태에서 새로운 디바이스 LG-F180 (옵티머스G) 를 읽고/쓰기해야 하면 // 1. LG-F180(Android_LG_OptimusG)를 구체적으로 읽는 객체/전략을 CReadAbstract 클래스의 파생 클래스로 만든다. // CReadAbstract는 추상클래스이기에 파생 클래스에서 인스턴스 생성하려면 read()를 구현해야 한다. class CRead_Android_LG_OptimusG : public CReadAbstract { public: void read() { cout << "I am reading Android_LG_OptimusG" << endl; } // 기타 attribute과 메소드들 }; // 2. LG-F180에 구체적으로 쓰는 객체/전략을 CWriteAbstract 클래스의 파생 클래스로 만든다. class CWrite_Android_LG_OptimusG : public CWriteAbstract { public: void write() // 이것을 구현해야 함. { cout << "I am writing Android_LG_OptimusG" << endl; } // 기타 attribute과 메소드들 }; // 만약 LG-F180에 구체적으로 쓰는 객체/전략이 Android_SSGalaxy3 와 똑 같다면 그냥 CWrite_Android_SSGalaxy3 을 상속하면 됨. //class CWrite_Android_LG_OptimusG : public CWrite_Android_SSGalaxy3 {}; // 3. CDevice 추상 클래스의 파생 클래스 형태로 Android_LG_OptimusG 클래스를 만든다. Android_LG_OptimusG instance // ------------------------ 구체적인 디바이스 타입들을 위의 Device 추상 클래스로부터 파생 ------------------------------ class Android_LG_OptimusG : public CDevice // CDevice는 abstract class { public: Android_LG_OptimusG() { readType = new CRead_Android_LG_OptimusG(); // initialize strategy that Android_SSGalaxy3 will use writeType = new CWrite_Android_LG_OptimusG(); } void getDeviceType() { cout << "나의 정체는 Android_LG_OptimusG" << endl; } // 기타 attribute과 메소드들 }; /* template<class T> class DeviceFactory { public: DeviceFactory(T deviceType) { } }; */ // simple no brain factory... 새로운 디바이스 타입이 생기면 여기에 추가해야 함. class CStaticDeviceFactory // 디바이스 인스턴스를 생성해 반환해 주는 넘..... { public: static CDevice * createDevice(string strDeviceType) { if (strDeviceType.compare("Android_SSGalaxy3") == 0) return new CAndroid_SSGalaxy3(); else if (strDeviceType.compare("Android_LG_OptimusG") == 0 ) return new Android_LG_OptimusG(); else if (strDeviceType.compare("iPhone4GS") == 0 ) return new CiPhone4GS(); } private: CDevice *deviceInstance; // 기타 attribute과 메소드들 }; // ----------------------- The main event ------------------------- int main() { // ----------- 1번 : 먼저 읽을 핸드폰 종류가 무엇인지 파악한다. // string deviceType_Read = CDevRecognizer.identifyDevice(); // CDevRecognizer : 디바이스가 어떤 타입인가를 인식해주는 클래스. 향후 구현해야 함 string deviceType_Read = "Android_SSGalaxy3"; // 테스트를 위해서 임시... // ----------- 2번 : 핸드폰 종류를 알았으니 그 핸드폰 객체를 생성한다. CDevice *deviceTo_Read = CStaticDeviceFactory::createDevice(deviceType_Read); // 객체생성공장으로 부터 맞는 객체를 생성해 가져온다. // ----------- 3번 : 핸드폰을 읽는다. if (deviceTo_Read == NULL) cout << "Wake Up! Put the right object before you call me. " << endl; else deviceTo_Read->readDevice(); // 이것은 바뀌지 않음. // dynamic_cast 를 이용해 실행 중에 다형성 변수에 있는 객체 타입을 subtype 객체로 바꾸는 연습. cout << endl << "dynamic_cast 활용 연습" << endl; CAndroid_SSGalaxy3* android_SSGalaxy3 = dynamic_cast<CAndroid_SSGalaxy3*>(deviceTo_Read) ; // dynamic_cast 실행. 만약 실패하면 NULL을 반환 if (android_SSGalaxy3 != NULL) // // base to derived class 로의 dynamic_cast가 성공했는지 확인. android_SSGalaxy3->testfn(); //dynamic_cast<Android_SSGalaxy3*>(device_Read)->testfn(); // base to derived class 로의 dynamic_cast가 성공하려면 base class가 polymorphic (abstract) 클래스이어야 함. dynamic_cast는 비용이 큼 // typeid 를 이용해 실행 중에 다형성 변수에 어떤 객체가 대응되어 있는 지 파악하는 연습 cout << endl << "typeid 활용 연습" << endl; cout << "deviceType_Read compile time type is : " << typeid(deviceTo_Read).name() << endl; cout << "deviceType_Read runtime dynamic type is : " << typeid(&*deviceTo_Read).name() << endl; cout << endl ; // ----------- 4번 : 쓰기 핸드폰을 인식한다. // string deviceType_Write = CDevRecognizer.identifyDevice(); // 디바이스가 어떤 타입인가를 인식했다는 것을 가정하고. string deviceType_Write = "iPhone4GS"; // ----------- 5번 : 쓰기 핸드폰을 생성한다 CDevice *deviceTo_Write = CStaticDeviceFactory::createDevice(deviceType_Write); //CDevice *deviceTo_Write = new iPhone4GS(); // 쓰기할 디바이스 // ----------- 6번 : 쓰기 핸드폰에 쓴다 deviceTo_Write->writeDevice(); cout << endl ; // runtime에 iPhone_4GS 의 쓰기 방식만을 변경함. cout << "iPhone_4GS의 write 방식만을 android_SSGalaxy3 방식으로 실행중에 변경 " << endl; deviceTo_Write->setWriteDevice(new CWrite_Android_SSGalaxy3() ); deviceTo_Write->readDevice(); deviceTo_Write->writeDevice(); // 읽어야 할 폰의 타입을 Android_LG_OptimusG 으로 변경 cout << endl << "인식을 잘 못 해서 읽는 폰을 Android_LG_OptimusG으로 변경 " << endl; deviceTo_Read->setReadDevice(new CRead_Android_LG_OptimusG) ; deviceTo_Read->readDevice();// cout << endl << "쓰기할 폰을 Android_LG_OptimusG으로 함 " << endl; deviceTo_Write = CStaticDeviceFactory::createDevice("Android_LG_OptimusG"); deviceTo_Write->writeDevice(); // 생성한 객체를 삭제. 메모리 누수 방지. delete deviceTo_Read; delete deviceTo_Write; int i; cin >> i; return 0; }