큰 스위치 문 : 잘못된 OOP?
나는 항상 큰 스위치 문이 잘못된 OOP 디자인의 증상이라는 의견을 가지고 있습니다. 과거에 저는이 주제를 논의하는 기사를 읽었으며 일반적으로 다형성을 기반으로하는 대체 OOP 기반 접근 방식을 제공하여 사례를 처리 할 올바른 개체를 인스턴스화했습니다.
나는 이제 프로토콜이 기본적으로 줄 바꿈으로 끝나는 명령과 데이터 줄, 끝 마커로 구성된 TCP 소켓의 데이터 스트림을 기반으로하는 괴물 같은 switch 문이있는 상황에 처해 있습니다. 이 명령은 100 개의 다른 명령 중 하나 일 수 있으므로이 괴물 switch 문을 더 관리하기 쉬운 것으로 줄이는 방법을 찾고 싶습니다.
내가 회상하는 해결책을 찾기 위해 인터넷 검색을 해봤지만 안타깝게도 Google은 요즘 많은 종류의 쿼리에 대해 관련없는 결과의 황무지가되었습니다.
이런 종류의 문제에 대한 패턴이 있습니까? 가능한 구현에 대한 제안이 있습니까?
한 가지 생각은 사전 조회를 사용하여 명령 텍스트를 개체 유형과 일치시켜 인스턴스화하는 것이 었습니다. 이것은 단순히 새 개체를 만들고 새 명령에 대해 새 명령 / 유형을 테이블에 삽입하는 좋은 이점이 있습니다.
그러나 이것은 유형 폭발의 문제도 가지고 있습니다. 이제 100 개의 새 클래스가 필요하며 데이터 모델에 깔끔하게 인터페이스 할 방법을 찾아야합니다. "하나의 진정한 스위치 문"이 정말로 갈 길인가?
귀하의 생각, 의견 또는 의견에 감사드립니다.
명령 패턴에서 약간의 이점을 얻을 수 있습니다 .
OOP의 경우 동작 변형이 충분히 작 으면 완전한 클래스 폭발을 피하기 위해 여러 유사한 명령을 각각 단일 클래스로 축소 할 수 있습니다 (예, OOP 전문가가 이미 그것에 대해 비명을 지르는 것을들을 수 있습니다). 그러나 시스템이 이미 OOP이고 100 개 이상의 명령 각각이 진정으로 고유 한 경우 고유 한 클래스를 만들고 상속을 활용하여 공통 항목을 통합하십시오.
시스템이 OOP가 아닌 경우 OOP를 추가하지 않을 것입니다. 간단한 사전 조회 및 함수 포인터를 사용하여 명령 패턴을 쉽게 사용할 수 있으며 명령 이름에 따라 동적으로 생성 된 함수 호출을 사용할 수도 있습니다. 언어. 그런 다음 논리적으로 연결된 함수를 유사한 명령 모음을 나타내는 라이브러리로 그룹화하여 관리 가능한 분리를 달성 할 수 있습니다. 이런 종류의 구현에 대해 좋은 용어가 있는지 모르겠습니다. URL 처리에 대한 MVC 접근 방식을 기반으로 항상 "디스패처"스타일로 생각합니다.
내가 가진 참조 두 개의 스위치 - 온 - 열거 형은 추상적 인 인터페이스가 다른 구현을 제공하는 여러 종류의 대체 될 수 있습니다 비 OO 디자인의 증상으로 스위치 문; 예를 들어, 다음 ...
switch (eFoo)
{
case Foo.This:
eatThis();
break;
case Foo.That:
eatThat();
break;
}
switch (eFoo)
{
case Foo.This:
drinkThis();
break;
case Foo.That:
drinkThat();
break;
}
...로 다시 작성해야합니다 ...
IAbstract
{
void eat();
void drink();
}
class This : IAbstract
{
void eat() { ... }
void drink() { ... }
}
class That : IAbstract
{
void eat() { ... }
void drink() { ... }
}
그러나 하나의 switch 문 은 switch 문을 다른 것으로 대체해야하는 강력한 지표가 아닙니다.
명령은 100 개의 다른 명령 중 하나 일 수 있습니다.
100 가지 중 하나를 수행해야하는 경우 100 방향 분기를 갖는 것을 피할 수 없습니다. 제어 흐름 (switch, if-elseif ^ 100) 또는 데이터 (문자열에서 명령 / 공장 / 전략으로의 100 개 요소 맵)로 인코딩 할 수 있습니다. 그러나 거기에있을 것입니다.
그 결과를 알 필요가없는 것들로부터 100-way 브랜치의 결과를 분리 할 수 있습니다. 아마도 100 가지 다른 방법이 괜찮을 것입니다. 코드를 다루기 어렵게 만들면 필요하지 않은 객체를 만들 필요가 없습니다.
I think this is one of the few cases where large switches are the best answer unless some other solution presents itself.
I see the strategy pattern. If I have 100 different strategies...so be it. The giant switch statement is ugly. Are all the Commands valid classnames? If so, just use the command names as class names and create the strategy object with Activator.CreateInstance.
There are two things that come to mind when talking about a large switch statement:
- It violates OCP - you could be continuously maintaining a big function.
- You could have bad performance: O(n).
On the other hand a map implementation can conform to OCP and could perform with potentially O(1).
I'd say that the problem is not the big switch statement, but rather the proliferation of code contained in it, and abuse of wrongly scoped variables.
I experienced this in one project myself, when more and more code went into the switch until it became unmaintainable. My solution was to define on parameter class which contained the context for the commands (name, parameters, whatever, collected before the switch), create a method for each case statement, and call that method with the parameter object from the case.
Of course, a fully OOP command dispatcher (based on magic such as reflection or mechanisms like Java Activation) is more beautiful, but sometimes you just want to fix things and get work done ;)
You can use a dictionary (or hash map if you are coding in Java) (it's called table driven development by Steve McConnell).
One way I see you could improve that would make your code driven by the data, so for example for each code you match something that handles it (function, object). You could also use reflection to map strings representing the objects/functions and resolve them at run time, but you may want to make some experiments to assess performance.
The best way to handle this particular problem: serialization and protocols cleanly is to use an IDL and generate the marshaling code with switch statements. Because whatever patterns (prototype factory, command pattern etc.) you try to use otherwise, you'll need to initialize a mapping between a command id/string and class/function pointer, somehow and it 'll runs slower than switch statements, since compiler can use perfect hash lookup for switch statements.
Yes, I think large case statements are a symptom that one's code can be improved... usually by implementing a more object oriented approach. For example, if I find myself evaluating the type of classes in a switch statement, that almost always mean I could probably use Generics to eliminate the switch statement.
You could also take a language approach here and define the commands with associated data in a grammar. You can then use a generator tool to parse the language. I have used Irony for that purpose. Alternatively you can use the Interpreter pattern.
In my opinion the goal is not to build the purest OO model, but to create a flexible, extensible, maintainable and powerful system.
I have recently a similar problem with a huge switch statement and I got rid off the ugly switch by the most simple solution a Lookup table and a function or method returning the value you expect. the command pattern is nice solution but having 100 classes is not nice I think. so I had something like:
switch(id)
case 1: DoSomething(url_1) break;
case 2: DoSomething(url_2) break;
..
..
case 100 DoSomething(url_100) break;
and I've changed for :
string url = GetUrl(id);
DoSomthing(url);
the GetUrl can go to DB and return the url you are looking for, or could be a dictionary in memory holding the 100 urls. I hope this could help anyone out there when replacing a huge monstrous switch statements.
Think of how Windows was originally written in the application message pump. It sucked. Applications would run slower with the more menu options you added. As the command searched for ended further and further towards the bottom of the switch statement, there was an increasingly longer wait for response. It's not acceptable to have long switch statements, period. I made an AIX daemon as a POS command handler that could handle 256 unique commands without even knowing what was in the request stream received over TCP/IP. The very first character of the stream was an index into a function array. Any index not used was set to a default message handler; log and say goodbye.
참고URL : https://stackoverflow.com/questions/505454/large-switch-statements-bad-oop
'Programing' 카테고리의 다른 글
프로덕션 환경에서 디버그 기호 (pdb 파일)를 배포하면 어떤 위험이 있습니까? (0) | 2020.10.20 |
---|---|
Vim은 Python 주석의 들여 쓰기를 자동으로 제거합니다. (0) | 2020.10.20 |
C ++에서 비어있는 std :: shared_ptr의 차이점은 무엇입니까? (0) | 2020.10.20 |
jquery에서 추가와 반대 (0) | 2020.10.19 |
루프에서 await를 사용하는 방법 (0) | 2020.10.19 |