سؤال

متى نمط الإستراتيجية يستخدم؟

أرى مقتطفات رمز العميل مثل هذا:


class StrategyExample {

    public static void main(String[] args) {

        Context context;

        // Three contexts following different strategies
        context = new Context(new ConcreteStrategyAdd());
        int resultA = context.executeStrategy(3,4);

        context = new Context(new ConcreteStrategySubtract());
        int resultB = context.executeStrategy(3,4);

        context = new Context(new ConcreteStrategyMultiply());
        int resultC = context.executeStrategy(3,4);

    }

}

ويبدو أنك قد تعيد عدلها فقط إلى هذا:


class StrategyExample {

    public static void main(String[] args) {
         // Three contexts following different strategies
        int resultA =new ConcreteStrategyAdd().execute(3,4);
        int resultB =new ConcreteStrategySubtract().execute(3,4);
        int resultC =new ConcreteStrategyMultiply().execute(3,4);
    }

}

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

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

المحلول

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

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

interface GraphStrategy {
    Image renderGraph(Data graphData);
}

class BigGraphStrategy implements GraphStrategy {
    ...
}

class SmallGraphStrategy implements GraphStrategy {
    ...
}

ثم في بعض الكود الأخرى:

GraphStrategy graphStrategy;

if (phoneBrowser == true) { 
    graphStrategy = new SmallGraphStrategy();
} else {
    graphStrategy = new BigGraphStrategy();
}

يمكن أن تستخدم بقية رمز التطبيق الخاص بك فقط graphStrategy.renderGraph() دون الحاجة إلى معرفة ما إذا كان يتم تنفيذ تقديم صور كاملة أو صغيرة.

نصائح أخرى

المناطق التي تتبادر إلى الذهن:

  • مصلح الموارد. في إدارة الموارد اليدوية، قد يقلل هذا من الوقت الذي يستغرقه المورد لتخصيصه أو تقليل التجزئة. تحتوي كل استراتيجية هنا على طريقة "تخصيص" لها نفس الواجهة، مع اتخاذ المستخدم اتخاذ قرار بشأن الاستراتيجية التي يجب استخدامها بناء على ما يحاولون تحسينه.
  • طريقة لتوصيل وإرسال بيانات الشبكة. ربما في بعض الحالات، تفضل الاتصال وإرسال Datagrams UDP، وربما في ماتينوس آخر حيث كان الأداء أقل من العامل الذي سترسله باستخدام TCP / IP.
  • استراتيجية تنسيق البيانات / التسلسل. اسمح للرمز بتحديد ما إذا كان يجب تسلسل كائن مع JSON أو مع XML. ربما واحدة للآلات، والآخر للحالات التي يمكن قراءة الإنسان. تحتوي كلا الاستراتيجيتين على طريقة "التسلسل" التي تأخذ كائن. كل تسليم بشكل مختلف.

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

الآن لماذا سيكون هذا أكثر فائدة من شيء مثل:

void DoIt()
{
    if (... situation1...)
    {
       DoA()
    }
    else
    {
       DoB();
    }
}

السبب في بعض الأحيان تريد فقط اتخاذ القرار مرة واحدة ونسيانها. النمط الرئيسي الآخر في النمط الاستراتيجي هو أنك تقفل القرار حول الاستراتيجية التي يجب استخدامها من التعليمات البرمجية التي تحتاج إلى تنفيذ الاستراتيجية.

DoItStrategy MakeDoItStrategy()
{
     if (... situation1...)
     {
           return new DoItStrategyA();
     }
     else
     {
           return new DoItStrategyB();
     }
}

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

على سبيل المثال، ضع في اعتبارك الحالة التي نقدم فيها قرارا على مستوى البرنامج، بناء على تكوين شبكة معطى، لتوصيل البيانات وإرسالها إلى مضيفين عن بعد مع UDP. بدلا من كل مستخدم لواجهة الشبكة التي تحتاج إلى معرفة المنطق باتخاذ القرار (وظيفة "Doit" أعلاه)، يمكننا إنشاء استراتيجية UDP مقدما وإصدارها إلى كل من يحتاج إلى إرسال بيانات الشبكة. هذه الاستراتيجية ثم تنفذ واجهة بسيطة مع نفس النتيجة النهائية - تحصل البيانات من A إلى B.

مقتطف التعليمات البرمجية الذي تقتبس عنه خادع قليلا في ذلك (قليلا) خارج السياق. ما تكتبه أدناه في مثالك هو أيضا نمط الاستراتيجية - لقد قمت بإعادة كتابة المثال أعلاه بشكل أكثر إغاثة.

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

  int mod = new ConcreteStrategy(){ 
         public int execute(int a, int b){ return a %b; }
  }.execute(3,4);

نعم، المثال عرجاء، ولكن مفهوم المندوبين مع استراتيجيات التنفيذ المختلفة هو نمط أساسي، قديم جدا، تصميم قد استخدمته في الكثير والكثير من التطبيقات.

أعتقد أن مشكلتك هنا هي واحدة مشتركة، على الرغم من أن كل مثال نمط تصميم تقريبا رأيته من أي وقت مضى مفتعلة بشكل لا يصدق ويقودني دائما لطرح الأسئلة تماما مثل لك - يبدو دائما عديمة الفائدة عند تقديمها بهذه الطريقة.

تستخدم أطر الكاكاو المستخدمة في Mac و iPhone نمط الاستراتيجية كثيرا, ، إلا أننا نسميها نمط المندوب. إليك كيفية استخدامها:

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

لذلك نعم، نمط الاستراتيجية هو واحد من مفضلاتي وأستعب كل يوم.

نمط الاستراتيجية مفيد في المواقف التي قد ترغب فيها (أو مستخدمي التعليمات البرمجية) في تغيير العمليات الحسابية في خوارزمياتك. مثال بسيط حيث استخدمت نمط الاستراتيجية في النمذجة الاستدلال في البحث *. A * يستخدم الاسلكية، التي هي حسابات بسيطة لتقدير التكلفة المتبقية إذا تم اختيار عقدة معينة (NI) على الطريق إلى عقدة الهدف (NG). بدا واجهة بلدي شيء من هذا القبيل:

class Heuristic {
public:
    virtual int estimateRemainingCost(const node &Ni, const node &Ng) const = 0;
};

وتستخدم مثل هذا:

...
// for each node (Ni) that is adjacent to the current node Nc
int node_priority = cost(Ni)/* the cost of choosing this node on the path */
                    + heuristic->estimateRemainingCost(Ni, Ng);
unsearched_nodes_.queue(node_priority, Ni);
...

الاستدلال هي استراتيجيات أو حسابات يمكن استبدالها. وبعبارة أخرى، فهو ممارسة تافهة لتغيير محاربة خوارزمية البحث.

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

طريقة واحدة للقيام بذلك (في Java):

Map<String, Strategy> strategyMap = new HashMap<String, Strategy>();
strategyMap.put("bark", new BarkingStrategy());
strategyMap.put("meow", new MeowingStrategy());
strategyMap.put("moo", new MooingStrategy());
strategyMap.put("giraffeSound", new UnknownStrategy());

يمكنك أولا بناء شكل من أشكال ذخارة الاستراتيجيات.

في وقت لاحق...

String command = //...some form of input
strategyMap.get(command).execute();

بهذه الطريقة، يمكنك "تعامل" بشكل عام "العديد من المواقف المختلفة.

بمعنى آخر:

moo

سوف تنفذ MooingStrategy()

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

إنه أكثر منطقية عندما يكون كائن السياق المزيد من المسؤوليات ودفاع التجريد استراتيجية هذه المسؤوليات من جانب بعض جوانب العملية. مثال واحد، (في C #) هو واجهة Icomparer:

interface IComparer<T>
{
    int Compare(T a, T b);
}

والتي يمكن أن تنتقل إلى خوارزمية الفرز.

الفرق الرئيسي هو أنه في المثال الثاني، فإن الاستراتيجية هي الخوارزمية (وبالتالي لا نمط). في المثال الأول، أنت تجريد / عزل جزء من الخوارزمية.

على سبيل المثال، تنفيذ Context.executeStrategy() ممكن ان يكون:

public int executeStrategy(int baseValue, int exponentFactor)
{
    return (int) Math.Pow(baseValue, this.Strategy.Calculate(2, exponentFactor));
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top