هل من الممكن تجنب مسبل?
-
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
واجهات برمجة التطبيقات ، على سبيل المثال من خلال التذرع 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);
}
}
...ومع ذلك تنفيذ تحتاج إلى مسبل كما هو موضح أعلاه.
سؤالي هو ، هل هناك طريقة للقيام بذلك دون الحاجة إلى مسبل في التنفيذ ؟ من قبل "هذا" ، أعني "تحديد UDTs مثل Word
و Canvass
والتي لا تعتمد على محددة ملموسة System
أنواع"?
بديل سيكون مجردة UDTs ...
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);
}
...ولكن هذا أيضا يحتاج مسبل في تنفيذ فرعية.
أنا أيضا فكرت في استخدام مزدوج إرسال المصطلح, ولكن ذلك يعتمد على تسمية مختلف الفئات الفرعية في واجهات برمجة التطبيقات.
أو إذا لم تكن مع واجهات أو فرعية ، هل هناك طريقة باستخدام المندوبين ؟
--تعديل:--
كانت هناك اثنين من الأجوبة المحتملة.
جواب واحد هو استخدام الأدوية بدقة كما اقترح سيدي Lantis' الإجابة أدناه ، كما اقترح بلوق وظيفة التي يوحنا السكيت مرتبطة.وأظن أن هذا من شأنه أن تعمل بشكل جيد في معظم الحالات.أسفل الجانب من وجهة نظري هو أنه يعني إدخال TFont
كنموذج المعلمة:ليس فقط فئة مثل Word
(الذي يحتوي على Font
سبيل المثال) التي يجب أن تصبح فئة عامة (مثل WordT<TFont>
) ...إنه أيضا أن أي الطبقة التي تحتوي على WordT<TFont>
(مثلا ، Paragraph
) الآن يحتاج أيضا إلى أن تصبح عامة مع TFont
المعلمة (مثلا ، ParagraphT<TFont>
).في نهاية المطاف, تقريبا كل فئة في الجمعية أصبحت فئة عامة.هذا لا الحفاظ على النوع السلامة وتجنب الحاجة إلى مسبل ... ولكن انها نوع من القبيح ، و يزعج وهم التغليف (الوهم بأن "الخط" هو معتم تنفيذ التفاصيل).
جواب آخر هو استخدام الخريطة أو قاموس في فئة المستخدم.بدلا من Font
في مكتبة القابل لإعادة الاستخدام, و بدلا من واجهة مجردة, تعريف 'تحمل' الفئة مثل:
public struct FontHandle
{
public readonly int handleValue;
FontHandle(int handleValue)
{
this.handleValue = handleValue;
}
}
ثم بدلا من downcasting من FontHandle
, ، Dictionary<int, Font>
المثال الذي الخرائط FontHandle
القيم Font
حالات.
المحلول
أولا ، أنا أتساءل عما إذا كان السيناريو بأكمله ليس قليلا الاصطناعي ؛ هل أنت حقا الذهاب الى تحتاج هذا المستوى من التجريد?ربما الاشتراك YAGNI?
لماذا MyGraphics
تعمل فقط مع MyFont
?يمكن أن تعمل مع 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
, أو فقط MyFont
?إذا كنت يمكن أن تجعل من العمل مع أي IFont
سوف يكون على ما يرام.وإلا قد تحتاج إلى النظر في معقد الأدوية لجعل كل وقت التحويل البرمجي اكتب آمنة.قد تجد موقعي على الأدوية في البروتوكول مخازن مفيدة مثل حالة مماثلة.
(الجانب اقتراح التعليمات البرمجية الخاصة بك سوف تكون أكثر واصطلاحا .صافي مثل إذا كنت تتبع تسمية الاتفاقيات التي تشمل باسكال الحالة على الطرق.)
أنا حاليا لا يدرك تماما C# بعد الآن - وقد مضى بعض الوقت الآن.ولكن إذا كنت لا تريد أن يلقي كل ما تبذلونه من الاشياء هناك ، قد يضطر إلى استخدام الأدوية.
يمكنني فقط تقديم كود جافا ، ولكن 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);
}