티스토리 뷰
크고 작은 실수가 몇개 있었다. 덕분에 엄청난 시행착오를 거쳐 오랜 시간걸렸다.
몇가지 실수를 적어 보겠다.
첫번째는 메세지 전달 부분에서의 의존성을 줄이지 못한 부분이다.
각 모듈을 DLL로 쪼개는 리팩토링을 감행 하였는데 여기서 각 DLL 간 의존성을 0으로 하고, 최대한 독립적으로 General 하게 코딩하려 했다.
A DLL에서 B DLL로 메세지를 전달 하는 과정에서 상수값을 어쩔 수 없이 공유하게 됐는데,
차라리 A DLL에 SetListener 메소드를 구현하고, B DLL에서 해당 메소드를 콜해서 원하는 핸들러를 등록하게 했어야 했다.
두번째는, Data와 Controller를 완벽하게 분리 하지 못했다.
기본적으로 Data 객체가 있고, 해당 객체를 컨트롤 하는 Controller를 만들어 두긴 했는데,
무슨 생각인지 Data 안에서 사용하는 vector를 바로 다른 곳에서 사용 가능하게 dllexport 해버렸다. (std::vector<T>& 를 리턴하는 Getter 제공)이렇게 하면 안됐고, 차라리 vector 안의 데이터에 접근하는 메소드 하나를 제공했어야 했다. 예를 들면 SetData(int nVectorIndex, T Data) 이런식으로 말이다.
지금 짠 코드는, Command 패턴이 많이 사용됐기 때문에 Command를 호출하는 곳에서
std::vector<T>& vecData = GetVecData();
vecData.push_back(t) .. 이렇게 Controller 외부에서 Data에 바로 접근하는 코드가 많이 들어가게 됐는데, 제대로 짰다면 해당 데이터를 처리 하기 위해서는 Controller의 Setter 부분을 호출해서 처리하는게 맞았다.
Command의 Execute 부분에서 바로 데이터를 처리하려다 보니, 나중에 멀티쓰레드에서 동기화 처리 하는데도 애를 꽤나 먹었다.
차라리 Controller에 동기화 처리를 해주면 한방에 해결되는 문제였는데..
세번째는 Action Tool와 Mode Tool의 모호성에 따른 세번에 걸친 리팩토링..
아직 이 부분은 해답을 내리진 못하겠다. 일단은 정리해보겠다.
보통 Tool을 만들 때, 아래의 상황을 고려한다.
1. Tool Button을 클릭 하는 순간 팝업 창 등이 뜨고, 값을 입력 한 후 OK 누르면 이벤트가 발생하는 경우.
2. 포토샵의 Marquee Tool, 룰러 툴 처럼 Mouse Down 시 이벤트 발생/ Mouse Up시 이벤트 끝 인 경우
3. 포토샵의 Path툴, Brush툴, 페인트 툴 처럼 매 마우스 이벤트 마다 자기 툴 이벤트를 계속 행해주는 경우
4. 그리고 제일 짜증나는.. Toggle Mode... 예를 들면 포토샵의 마스크툴 같은 경우다.. 마스크 모드 일 때랑 아닐 때랑 툴들의 역할이 달라진다..
1의 경우는 Tool Button을 클릭하는 순간- 까지는 Command로 처리후, 값 입력 후 OK 누르면 처리되는 이벤트 역시 Command로 처리한다.
그래서 이걸 Tool이라고 부르는게 맞나 의문이다. View에 마우스 클릭이 필요 없기 때문에..
그림판의 새 도화지'/'파일 열기' 등이 이에 해당한다. 그래서 이런 경우는 굳이 Tool로 분류하지도 않고, UI적으로도 Tool Box에 배치시키지 않는다. 따로 배치한다.
2와 3의 경우는 잘 생각해야한다.
사각형 영역을 선택하는 방법은 여러가지가 있지만 일단 대충 2개만 생각해보자.
첫번째는 좌상단을 LButtonDown 한 후 우하단으로 드래그 하여 사각형을 그리고 LButtonUp 하는 것이다.
두번째는 좌상단을 클릭(LButtonDown&Up 동시), 우 하단을 클릭 하여 설정 하는 것.(마치 Path툴 처럼) 이 모두는 Action Tool 로 관리한다.
ActionTool에 Start / Doing / End 를 만들어서 처리한다. 그리고 각각의 Trigger용 method도 설정한다. Trigger용 메소드를 이벤트 리스너로 등록가능하게 해서 처리한다.
Start는 말그대로 ActionTool의 Start다. ActionTool로 설정 된 이후 View에 마우스 이벤트가 최초로 들어왔을 경우 발생시킨다. (SetStartListner를 해당 위치에 등록함. 툴의 성격에 따라 LButtonDown을 Start의 Trigger로 할지, Up을 Trigger로 할지 정한다.)
Doing은 보통 MouseMove 이벤트 발생시에 동작하도록 리스너를 등록한다, 혹은 첫번째 방법대로 할 경우는 딱히 해 줄 것은 없고, 두번째 방법 대로 할 경우에는 추후 들어오는 클릭 이벤트를 모두 체크한다.
End용 Trigger 역시 클릭 이벤트가 들어오는 방법에 따라서 End 조건을 만족하는지 체크한다. (Path Tool이 폐곡선을 그렸다던지, 더블클릭을 했다던지)
참고로 End가 Call 됐다고 해서 ActionTool이 다른것으로 바뀌진 않는다. 클릭 이벤트가 다시 들어올 경우 Start부터 다시 시작하면 되는 것이다.
4의 경우는 Mode Tool로 관리한다.
Mode가 Enable/Disable 돼 있는 경우에 따라서 발생하는 이벤트가 모두 다르다.
이 때, 중복 가능한 Mode가 있고 하나만 선택 가능한 Mode가 있기 때문에, Mode 를 Group으로 묶는다. (ModeGroup)
같은 ModeGroup 안에 있는 Mode들 끼리는 중복이 안된다. 마치 RadioBox 처럼.
Mode의 경우 모드가 Enable 됐는지 안돼있는지 알기 위해서는 Flag를 체크해야 하기 때문에, 위에서 만든 Action들이 처리 될 때 마다
ModeManager를 Broadcast해서 Enable 여부를 파악해, Event를 Trigging 해야 한다.
생각나는 대로 적어봤지만 여전히 햇깔린다.결론은.. 마우스 클릭 이벤트로 처리 가능한 웬만한 애들은 ActionTool. 아 참고로 ActionTool간의 중복은 당연히 안된다.
Toggle 가능한 툴들은 ModeTool,
마우스클릭 굳이 필요 없는 툴 들은 그냥 Command로 처리.
마지막으로.. 필터/알고리즘 적용 등을 처리 하는 경우 웬만하면 Async Thread말고 Sync Thread로 처리하자.
Async로 해놓는 동안 View에 이벤트가 발생한다던지 Data가 바뀐다던지 하면 처리하기 곤란하다..이것 역시 Command로 만들어서, Sync로 생성 후 Progress Dlg로 처리하는 방법이 편하다.
아.. 하나 더 생각났다.
로직 처리를 Command에 맡기자. 로직을 DLL에서 처리시켜려다 보니 DLL이 다른 프로젝트와의 의존도가 계속해서 높아지더라.
예를 들면 파일로 부터 값을 읽어서 vector<Data>에 저장해야 하는게 있고, FileController를 DLL로 빼두었다면,
설계 가능한 방법은 아래와 같이 두개다.
1. vector<Data> 자체를 FileController로 넘기는 것.
2. Main의 Logic 부분에서 FileController로 읽은 값을 vector<Data>에 넣는것.
지금 짠 플젝은 아무 생각 없이 1번으로 했는데, 그러다 보니 vector<Data>가 자꾸만 여기저기서 불려서 의존성이 높아진다.
차라리 2번 방법으로 할 걸 그랬다. 그러면 FileController 자체도 최대한 범용으로 쓸 수 있고 좋다.(꼭 vector<Data> IO 용이 아니라 다른 용도로 쓸 수 있을 듯)
너무 두서 없이 적었지만 대충 생각 나는건 이정도..
그렇다고 너무 작은 소규모 프로젝트에서 처음부터 이런 설계를 적용하는 건 배보다 배꼽이 커진다. 특히 Mode Tool의 설계는, 언제든지 집어 넣을 수 있을 수준으로만 설계하고 당장 넣을 필요는 없다.
아.. 갑자기 생각 난건데.. Action Tool에서 최종적으로 처리가 되는 부분은 결국 Command 패턴 설계를 집어넣어야 한다.
Undo/Redo를 위해서...
오늘은 여기 까지... 나중에 다시 설계할 일 있으면 다시 곰곰히 생각해야지..''
'
'JHB > 잡다한 이야기' 카테고리의 다른 글
카테고리 정하기가 어렵다 (0) | 2019.12.15 |
---|---|
소스코드 해독을 위한 팁 (1) | 2019.05.10 |
로직부분 짜기. 적절한 함수의 최소 단위는..? (0) | 2018.06.25 |
host - client 시스템 설계시 행한 오늘의 삽질 (0) | 2018.05.10 |
[세미나] 유니코드의 다양한 이해 (0) | 2014.09.07 |
대학을 졸업한 소프트웨어 개발자에게 중요한 능력 (0) | 2013.04.26 |
[잡설] ResourcePool을 사용하자.. (0) | 2012.07.24 |
경력있는 프로그래머는 무엇이 다른가? (0) | 2011.09.16 |
- Total
- Today
- Yesterday
- 프로그래밍
- source
- Python
- 리눅스
- 음악
- it
- database
- Quiz
- kering
- jni
- jni강좌
- gcc
- C++
- MFC
- driver
- 안드로이드
- android
- AWS
- Troubleshooting
- Visual C++
- db
- 드라이버
- API
- algorithm
- NDK
- C
- winapi
- java
- linux
- Cloud
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |