قوالب GRASP: خبير معلومات

مرحباً ، هابروفسك. على اتصال فلاديسلاف رودين. أقوم حاليًا بتدريس دورات حول هندسة البرمجيات وهندسة البرمجيات ذات الحمولة العالية على بوابة OTUS. قررت هذه المرة كتابة القليل من مواد حقوق النشر تحسبًا لبدء دورة جديدة "أنماط الهندسة المعمارية والتصميم" . استمتع بالقراءة.





المقدمة


موصوف في كتاب Craig Larman تطبيق UML والأنماط ، الإصدار الثالث ، أنماط GRASP هي تعميم لأنماط GoF ، وكذلك نتيجة مباشرة لمبادئ OOP. إنها تكمل الخطوة المفقودة في السلم المنطقي ، والتي تسمح لك بالحصول على أنماط GoF من مبادئ OOP. من المرجح ألا تكون قوالب GRASP أنماط تصميم (مثل GoF's) ، ولكن المبادئ الأساسية لتوزيع المسؤولية بين الطبقات. تُظهر الممارسة أنها ليست شائعة جدًا ، ومع ذلك ، فإن تحليل الفصول المصممة باستخدام مجموعة كاملة من أنماط GRASP هو شرط أساسي لكتابة كود جيد.

تتكون القائمة الكاملة لقوالب GRASP من 9 عناصر:

  • خبير معلومات
  • المنشئ
  • مراقب
  • اقتران منخفض
  • تماسك عالية
  • تعدد الأشكال
  • تلفيق نقي
  • التوجيه
  • الاختلافات المحمية

أقترح النظر في النمط الأكثر وضوحا وأهم من القائمة: خبير المعلومات.

خبير معلومات


الصياغة


تجنب الصيغ العلمية ، يمكن التعبير عن جوهر هذا النمط على النحو التالي: يجب معالجة المعلومات حيث تحتوي عليها.

مثال على الانتهاك


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

ضع في اعتبارك أبسط نظام فئة: الطلب (الطلب) ، الذي يحتوي على قائمة OrderItem'ov (بنود الطلب) ، والتي تحتوي عناصرها بدورها على السلعة الجيدة (المنتج) وكميتها ، وقد يحتوي المنتج ، على سبيل المثال ، السعر والاسم ، وما إلى ذلك:

@Getter
@AllArgsConstructor
public class Order {
    private List<OrderItem> orderItems;
    private String destinationAddress;
}

@Getter
@AllArgsConstructor
public class OrderItem {
    private Good good;
    private int amount;
}

@Getter
@AllArgsConstructor
public class Good {
    private String name;
    private int price;
}


لدينا مهمة بسيطة: لحساب كمية الطلب. إذا اقتربت من حل هذه المشكلة بشكل غير مدروس ، يمكنك كتابة شيء من هذا القبيل على الفور في رمز العميل الذي يعمل مع كائنات من فئة Order:

public class Client {
    public void doSmth() {
        
    }
    
    private int getOrderPrice(Order order) {
        List<OrderItem> orderItems = order.getOrderItems();
        
        int result = 0;
        
        for (OrderItem orderItem : orderItems) {
            int amount = orderItem.getAmount();
            
            Good good = orderItem.getGood();
            int price = good.getPrice();
            
            result += price * amount;
        }
        
        return result;
    }
}


دعونا نحلل هذا الحل.

أولاً ، إذا بدأنا في إضافة منطق الأعمال المتعلق بالتسعير ، فإن رمز طريقة Client :: getOrderPrice لن ينمو حتمًا فحسب ، بل سيصبح أيضًا محاطًا بجميع أنواع if-s (خصم للمتقاعدين ، خصم على الإجازات ، خصم بسبب مشتريات الجملة) ، والتي ستؤدي في النهاية إلى حقيقة أنه من المستحيل قراءة هذا الرمز ، ناهيك عن التغيير.

ثانيًا ، إذا قمت بإنشاء رسم تخطيطي لـ UML ، يمكنك أن تجد أن هناك تبعية لفئة العميل على 3 فئات: Order و OrderItem و Good. يجذب كل منطق العمل للعمل مع هذه الفئات. هذا يعني أنه إذا أردنا إعادة استخدام OrderItem أو Good بشكل منفصل عن Order (على سبيل المثال ، لحساب سعر البضائع المتبقية في المستودعات) ، فإننا ببساطة لا نستطيع القيام بذلك ، لأن منطق الأعمال يكمن في رمز العميل ، مما سيؤدي إلى تكرار لا مفر منه للشفرة.

في هذا المثال ، كما هو الحال في كل مكان تقريبًا حيث توجد سلسلة من get'ov ، يتم انتهاك مبدأ خبير المعلومات ، لأن كود العميل يعالج المعلومات ويحتوي على أمره.

مثال تطبيقى


دعونا نحاول إعادة توزيع المسؤوليات وفقًا للمبدأ:

@Getter
@AllArgsConstructor
public class Order {
    private List<OrderItem> orderItems;
    private String destinationAddress;
    
    public int getPrice() {
        int result = 0;
        
        for(OrderItem orderItem : orderItems) {
            result += orderItem.getPrice();
        }
        
        return result;
    }
}

@Getter
@AllArgsConstructor
public class OrderItem {
    private Good good;
    private int amount;

    public int getPrice() {
        return amount * good.getPrice();
    }
}

@Getter
@AllArgsConstructor
public class Good {
    private String name;
    private int price;
}

public class Client {
    public void doSmth() {
        Order order = new Order(new ArrayList<>(), "");
        order.getPrice();
    }
}


الآن تتم معالجة المعلومات في الفصل الذي يحتوي عليها ، ويعتمد رمز العميل فقط على الطلب ، ولا يشك في أي شيء حول هيكله الداخلي ، ويمكن تجميع الفئات Order ، OrderItem and Good ، أو OrderItem and Good في مكتبة منفصلة يمكن استخدامها في أجزاء مختلفة من المشروع.

استنتاج


خبير المعلومات ، الناتج عن التغليف ، هو أحد أهم المبادئ الأساسية لتقاسم مسؤولية GRASP. يمكن تحديد انتهاكها والقضاء عليه بسهولة من خلال زيادة بساطة إدراك الكود (مبدأ المفاجأة الأقل) ، مما يضيف إمكانية إعادة الاستخدام وتقليل عدد الاتصالات بين الفئات.

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

All Articles