سؤال

لدي بعض المنطق الذي يحدد ويستخدم بعض الأنواع المعرفة من قبل المستخدم, مثل هذه:

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);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top