은 그것을 피하기 위하여 가능한 풀이 죽?
-
20-08-2019 - |
문제
나는 몇 가지 논리,정의와 사용하는 일부 사용자 정의 형식은 이들을 좋아한다:
class Word
{
System.Drawing.Font font; //a System type
string text;
}
class Canvass
{
System.Drawing.Graphics graphics; //another, related System type
... and other data members ...
//a method whose implementation combines the two System types
internal void draw(Word word, Point point)
{
//make the System API call
graphics.DrawString(word.text, word.font, Brushes.Block, point);
}
}
로직을 수행 한 후,계산이 유형(예를들면서로를 찾는 Word
인스턴스),간접적으로 사용 System
APIs,예를 들어를 호출하여 Canvass.draw
방법입니다.
나는 다음과 같이 논의 독립 System.Drawing
네임스페이스:대부분을 돕기 위해,단위 테스트(나는 생각한 단위 테스트를 출력하는 것이 더 쉽지 확인하는 경우 draw
방법 그리기가 아닌 다른 부 System.Drawing.Graphics
인스턴스).
을 제거하고 논리의 속국에서 System.Drawing
네임스페이스,나는 선언하는 몇 가지 새로운 인터페이스의 자리 표시자로 System.Drawing
유형,예를 들어:
interface IMyFont
{
}
interface IMyGraphics
{
void drawString(string text, IMyFont font, Point point);
}
class Word
{
IMyFont font; //no longer depends on System.Drawing.Font
string text;
}
class Canvass
{
IMyGraphics graphics; //no longer depends on System.Drawing.Graphics
... and other data ...
internal void draw(Word word, Point point)
{
//use interface method instead of making a direct System API call
graphics.drawText(word.text, word.font, point);
}
}
는 경우 내가 이것 다음,다른 어셈블리할 수 있는 다른 구현 IMyFont
고 IMyGraphics
인터페이스 예를 들어,...
class MyFont : IMyFont
{
System.Drawing.Font theFont;
}
class MyGraphics : IMyGraphics
{
System.Drawing.Graphics theGraphics;
public void drawString(string text, IMyFont font, Point point)
{
//!!! downcast !!!
System.Drawing.Font theFont = ((MyFont)font).theFont;
//make the System API call
theGraphics.DrawString(word.text, theFont, Brushes.Block, point);
}
}
...그러나 이 구현에 필요한 낙심과 같이다.
나의 질문은, 이 있을 할 수 있는 방법 이 필요로하지 않고 낙심에서 구현? 에 의해"이"나는 말은"정의는 다음과 같 Udt Word
고 Canvass
는지에 따라 특정 콘크리트 System
형식"은?
는 대안이 될 것 이라고 추상적인 Udt...
class Word
{
//System.Drawing.Font font; //declared in a subclass of Word
string text;
}
class Canvass
{
//System.Drawing.Graphics graphics; //declared in a subclass of Canvass
//concrete draw method is defined in a subclass of Canvass
internal abstract void draw(Word word, Point point);
}
...하지만 이것은 너무 필요 낙심에서의 구현은 서브 클래스.
또한 나의 생각을 사용하여 더블 dispatch 관용구,하지만 그에 따라 이름 지정이 다양한 서브 클래스에서 일으킬 수 있습니다.
거나,하지 않을 경우 인터페이스 또는 하위 클래스가 어떤 방법을 사용하여 대리인?
--편집:-
이 있었다 가능한 두 개의 답변이 있습니다.
하나 응답이 제네릭을 사용하기 위해,정확하게로에 의해 제안된'선생님 란티스'답변을 아래와 같이 제 블로그 글을 존 스키트 연결되어 있습니다.생각이 잘 작동에서 대부분의 시나리오.아래쪽 나의 관점에서 다는 것을 의미한 소개 TFont
템플릿으로 매개변수:그것은뿐만 아니라 다음과 같이 클래스 Word
(을 포함하는 Font
인스턴스)는 요구되는 일반적인 클래스(아 WordT<TFont>
)...그것은 또한 모든 등을 포함하는 WordT<TFont>
(예: Paragraph
이)이제 필요가 일반으로 TFont
매개 변수(예를들면 ParagraphT<TFont>
).결국,거의 모든 클래스에서는 어셈블리가 일반적인 클래스입니다.이 가 유형-안전 및 피해야 downcast... 지 그것의 종류의 추악,그리고 방해 환상의 캡슐에 넣기(는 환상'폰트'는 불투명한 세부 구현).
또 다른 대답을 사용하는 것도 또는 사전에 사용자는 클래스입니다.대 Font
에서 재사용할 수 있는 라이브러리 대신,추상 인터페이스를 정의한'처'등 다음과 같:
public struct FontHandle
{
public readonly int handleValue;
FontHandle(int handleValue)
{
this.handleValue = handleValue;
}
}
그런 다음 대신에서 downcasting FontHandle
, 지 Dictionary<int, Font>
인스턴스는 지도 FontHandle
값 Font
인스턴스가 있습니다.
해결책
첫째, 전체 시나리오가 약간 인공적이지 않은지 궁금합니다. 당신은 정말로 가고 있습니까? 필요 이 추상화 수준? 아마도 구독 할 것입니다 야그니?
왜 당신의 MyGraphics
a MyFont
? AN과 함께 작동 할 수 있습니다 IFont
? 그것은 인터페이스를 더 잘 활용 하고이 전체 문제를 피할 것입니다 ...
하나의 옵션은 약간의 재 설계 일 수 있으므로 IFont
글꼴의 메타 데이터 (크기, 글꼴 팬 스페이스 등)를 설명하면 콘크리트에 물건이 있습니다. MyGraphics
처럼:
[public|internal] MyFont GetFont(IFont font) {...} // or just Font
그리고 번역을하는 것은 그래픽의 작업이됩니다. 따라서 다음과 같은 것을 사용했습니다.
public void drawString(string text, IMyFont font, Point point)
{
using(System.Drawing.Font theFont = GetFont(font))
{
theGraphics.DrawString(word.text, theFont, Brushes.Block, point);
}
// etc
}
물론, Point
번역이 필요할 수 있습니다; -p
다른 팁
당신은 효과적으로 "컴파일러보다 더 잘 알고 있습니다 - 나는 그것이 인스턴스가 될 것임을 알고 있습니다. MyFont
. "그 시점에서 당신은 가지고 있습니다 MyFont
그리고 MyGraphics
다시 단단히 결합되어 인터페이스의 점이 약간 줄어 듭니다.
해야 한다 MyGraphics
어떤 것과 함께 일하십시오 IFont
, 또는 a MyFont
? 당신이 그것을 만들 수 있다면 IFont
너는 괜찮아 질거다. 그렇지 않으면 복잡한 제네릭을보고 모든 컴파일 타임 유형을 안전하게 만들어야 할 수도 있습니다. 당신은 찾을 수 있습니다 프로토콜 버퍼의 제네릭에 대한 내 게시물 비슷한 상황으로 유용합니다.
(측면 제안 - 방법에 대한 파스칼 사례를 포함하는 이름 지정 규칙을 따르는 경우 코드가 더 특이 적으로 .NET -like입니다.)
나는 현재 C#을 더 이상 잘 모르고 있습니다. 지금은 시간이 지났습니다. 그러나 모든 물건을 거기에 캐스팅하고 싶지 않다면 제네릭을 사용해야 할 수도 있습니다.
Java 코드를 제공 할 수 있지만 C#은 where
예어.
인터페이스를 일반 인터페이스로 만드십시오. 자바에서
IMyGraphics<T extends IMyFont>
그리고 MyGraphics : IMyGraphics<MyFont>
그런 다음 재정의하십시오 drawString
취할 서명 T font
대신 두 번째 매개 변수로 IMyFont
. 이것은 당신이 쓸 수있게해야합니다
public void drawString(string text, MyFont font, Point point)
당신의 직접 MyGraphics
수업.
C#에서 IMyGraphics<T extends IMyFont>
해야한다 public interface IMyGraphics<T> where T:IMyFont
, 그러나 나는 그것에 대해 100% 확실하지 않습니다.
지에서 캐스팅 IFont
하기 MyFont
?당신이 작업을 수행 할 수 있습니다:
interface IFont {
object Font { get; }
}
class MyFont : IFont {
object Font { get { return ...; } }
}
물론 당신은 여전히 해야에서 캐스팅 System.Object
하기 System.Drawing.Font
에서 드로잉 방법,그러나 당신은 단지 제거에 대한 의존도는 특정 클래스 구현(MyFont
).
public void DrawString(string text, IFont font, Point point)
{
System.Drawing.Font f = (Font)font.Font;
graphics.DrawString(text, f, Brushes.Block, point);
}