كيف يجب أن أقوم بتصميم معطف مع حالات متعددة؟

StackOverflow https://stackoverflow.com/questions/2134328

  •  22-09-2019
  •  | 
  •  

سؤال

لدي كيان ، أي ما يعادل مهمة ، يمكن أن يكون لها حالات متعددة. يمكن أن تكون "المهمة" في حالة معلقة أو تم تمريرها أو فاشلة. سيكون لكل واحدة من هذه الحالات أيضًا بعض البيانات الفريدة. على سبيل المثال ، في حالة فاشلة ، يجب أن يكون للكيان سبب للفشل ، في حالة المعلقة يجب أن يكون له موعد نهائي للتقييم ، إلخ.

في حين أن ما سبق يقودني إلى الاعتقاد بأنني يجب أن يكون لدي كائن منفصل لتمثيل كل ولاية ، إلا أن المعرف الأساسي للكيان يجب أن يظل كما هو ، مما يدفعني نحو التفكير في هذا ككائن واحد.

أيضا ، الانتقال من ولاية إلى أخرى يتطلب بعض المنطق. سيتم التعامل مع المهمة "المعلقة" إلى حالة "تم تمريرها" بشكل مختلف عن مهمة "فاشلة" في إجراء نفس الانتقال.

إذا كانت تمثيلات كل ولاية هي نفسها بالضبط ، فسأستخدم خاصية بدائية وأقوم بها. ومع ذلك ، نظرًا لأن الدول المختلفة لها تمثيلات مختلفة قليلاً ، فقد كنت أعاني من أجل اكتشاف أفضل طريقة لنمذجة ذلك. إن منطق تحكم الدولة الداخلية أصبح فوضويًا قليلاً ، لذا اعتقدت أنني سأعود إلى الوراء وأعيد النظر. أي أفكار؟

أنا أستخدم C# على الرغم من أنني سأعتبر هذه اللغة غير الملحوظة.

هل كانت مفيدة؟

المحلول

عندما قرأت هذا السؤال لأول مرة ، كانت إجابتي هي استخدام التعداد لتحديد الدولة. بعد إعادة قراءة ذلك ، أود أن أقترح أحد ما يلي:

  1. قم بتنفيذ كل مهمة كفئة منفصلة (PendingTask ، PassedTask ، ...) مع نفس الوالد ، ومُنشئ يقبل مهمة من أي نوع يأتي قبل تلك الحالة.
  2. قم بتنفيذ مهمة واحدة ، وإنشاء فئة جديدة TaskStatedata مع فئات الأطفال للبيانات اللازمة لكل ولاية.
  3. قم بتنفيذ مهمة واحدة ، ولكن لديك طريقة منفصلة لتغيير الحالة لكل نوع حالة مع معلمات للسمات الإضافية اللازمة لتلك الحالة

أقترح هذه الحلول لضمان سلامة البيانات.

نصائح أخرى

الذهاب مع نهج الكائن النقي الموجهة نحو الجوانب السلبية. ما لم تكن تخطط للقيام بالكثير من إدارة الكود البوليمفي ، تجنب تمثيل حالة فئة المجال باستخدام تعدد الأشكال مباشرة. لقد استقرت على نهج أكثر هجينة. تحتاج الحالات المختلفة إلى صياغة كشجرة ميراث من الوالدين/الطفل. ابدأ مع بنية قاعدة مجردة من الصوفية التي لديها أطفال Mystate1 و Mystate2 و Mystate3. ( يرى إجابة جيفري على سبيل المثال.

الكيان الذي يحتاج إلى تعقب حالته لديه سمة من "الحالة الحالية" والتي هي من نوع الغموض. عندما يغير الكيان ، يكون هذا أمرًا بسيطًا أو استدعاء Setter () لتغييره. يمكنك بناء مثيلات مفردة لكل ولاية إذا رغبت في ذلك ، أو إنشاء مثيلات جديدة لكل تغيير. يعتمد ذلك على عدد المرات التي تتغير فيها الدولة وعدد الكائنات التي يتم تتبع حالتها. إذا أصبحت الأرقام كبيرة جدًا ، فيمكنك التفكير في نهج Singleton.

يمكن أن تكون "المهمة" في حالة معلقة أو تم تمريرها أو فاشلة. سيتم التعامل مع المهمة "المعلقة" إلى حالة "تم تمريرها" بشكل مختلف عن مهمة "فاشلة" في إجراء نفس الانتقال.

هذا يبدو مجموعة غريبة إلى حد ما من الدول. أتوقع أن تستغرق المهمة بعض الوقت للتنفيذ ، لذا الانتقال من المعلق إلى التنفيذ ، ثم تمرير أو تفشل ، والمهام التي لا تنفذ قبل نفاد وقتها. إذا كان هناك أيضًا انتقال من فشل إلى الفشل والتعبير ، فقد يضيف ذلك حالة أخرى.

ارسم آلة الدولة للعثور على الدول.

أولاً ، هل تحتاج إلى تصميم الدول من الناحية الهيكلية؟ هل سيفعل العلم المعلق/منتهية الصلاحية ، والوقت المحدد والنتيجة (مع الفشل والنجاح كنوعين من النتيجة)؟ ماذا يحتاج عملاء المهمة منه؟

ثانياً ، هل تتفاعل مع مهمة أم جدولة؟ ليس من غير المألوف إعطاء وصف للمهمة لجدولة واستعادة المستقبل ، والتي يمكن الاستعلام عنها حول نتيجة المهمة. لكن المهمة نفسها لا تتعرض ، فقط سواء أكملت والنتيجة. إذا كنت بحاجة إلى تقدم ، فقد ترغب في أن يكون لديك جدولة يمكن الاستعلام عنها حسب معرف المهمة للحصول على تقدم ، بدلاً من كائن مهمة تحتفظ به - وجود كائن مهمة يغير الحالة بشكل متزامن يجعل من الصعب الحصول على مجموعة متسقة من بيانات الدولة منها ، حتى تصل إلى حالة نهائية. إذا لم يكن للحالة "التي تم تمريرها" معلومات فاشلة ، فإن الاستعلام "هل فشلت" ، يليه "الحصول على حالة الفشل" يؤدي بسهولة إلى سباق ما لم تكن لديك قفل خارجي (EWWW) ، لذا فإن إرجاع معلومات مهمة غير قابلة للتغيير تصبح ذرية مرغوب فيه ، في الوقت الذي يصبح فيه كائن مهمتك ما يعادل أكثر أو أقل من المعرف الذي تم تمريره إلى جدولة.

ألق نظرة على نمط الدولة.

هذا يبدو وكأنه تطبيق مثالي لميراث الكائن وتعدد الأشكال.

abstract class Task
{
      public int TaskId { get; private set; }
      abstract PassedTask TransitionToPassed();
      ...
}

class PendingTask : Task
{
      PassedTask TransitionToPassed()
      {
            PassedTask passed = new PassedTask();
            passed.TaskId = TaskId;
            ...
            return passed;
      }
      ...
}

class PassedTask : Task
{
      PassedTask TransitionToPassed()
      {
            return this;
      }
      ...
}

class FailedTask : Task
{
      public string ReasonForFailure { get; private set; }
      PassedTask TransitionToPassed()
      {
            ...
      }
      ...

}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top