تضخم الواجهة.
تضخم الواجهة نمط مضاد تتراكم فيه العمليات في واجهة واحدة حتى يصبح كل عميل معتمداً على دوال لا يستخدمها أبداً.
تضخم الواجهة (Interface bloat) نمط مضاد تتراكم فيه في واجهة واحدة – سواء كانت الواجهة البرمجية العامة لكلاس، أو interface في Java أو C#، أو الدوال التي تصدّرها وحدة برمجية – عمليات تفوق بكثير ما يحتاجه أي عميل من عملائها.
التعريف
تُستخدم كلمة «التضخم» (bloat) لوصف مشكلات عديدة في تصميم البرمجيات، منها واجهات المستخدم المثقلة بالميزات والخيارات. أما في تصميم الواجهات البرمجية (API)، فتكون الواجهة متضخمة عندما تجمع خلف نوع واحد مسؤوليات متعددة لا رابط بينها: الاستعلام والحفظ والإدارة والإشعارات. عندئذ يتعين على كل تطبيق للواجهة توفيرها جميعاً، ويعتمد كل عميل على عمليات لا يستدعيها أبداً، فيؤدي أي تغيير في إحداها إلى إعادة ترجمة عملاء لا علاقة لهم بها أو إعادة اختبارهم أو نشرهم.
من العلامات المعتادة للواجهة المتضخمة:
- تطبيقات تترك دوالاً فارغة أو ترمي
UnsupportedOperationException؛ - بدائل اختبار يجب أن تحاكي عشرات الدوال من أجل اختبار يستخدم دالة واحدة؛
- مجموعات من الدوال يستخدمها عملاء مختلفون وتتغير لأسباب مختلفة.
مبدأ فصل الواجهات
العلاج هو مبدأ فصل الواجهات (حرف «I» في SOLID): لا ينبغي إجبار أي عميل على الاعتماد على دوال لا يستخدمها. تُقسَّم الواجهة المتضخمة إلى عدة واجهات صغيرة، تختص كل منها بدور واحد. ويمكن لكلاس واحدة أن تطبّقها جميعاً، لكن كل عميل لا يرى إلا الدور الذي يحتاجه.
عادةً ما تكون إعادة الهيكلة استخراج الواجهة هي الخطوة الأولى. وكثيراً ما تختبئ خلف الواجهة المتضخمة كائن إله أو كلاس ضخمة يجب تقسيمها هي الأخرى.
مثال
يخلط UserRepository هذا بين الاستعلام والحفظ والاستعلامات الإدارية ورسائل إعادة تعيين كلمة المرور. وصفحة الملف الشخصي التي لا تستدعي سوى byId تظل معتمدة على الدوال السبع كلها، ويجب على كل كائن وهمي في اختباراتها أن يطبّقها.
interface UserRepository {
User byId(int id);
User byEmail(String email);
void save(User user);
void delete(int id);
void rename(int id, String name);
List<User> admins();
void sendReset(int id);
}
بعد التقسيم حسب الدور، لا يعتمد كل عميل إلا على ما يستدعيه:
interface UserLookup {
User byId(int id);
User byEmail(String email);
List<User> admins();
}
interface UserStore {
void save(User user);
void delete(int id);
void rename(int id, String name);
}
interface PasswordReset {
void sendReset(int id);
}
class ProfilePage {
private final UserLookup users; // يعتمد فقط على ما يستخدمه
ProfilePage(UserLookup users) { this.users = users; }
String title(int id) { return users.byId(id).name(); }
}
لا يزال بإمكان كلاس واحدة مثل JdbcUsers أن تطبّق UserLookup وUserStore معاً؛ ما تقلّص هو اعتماديات العملاء فقط.
مقتبس من مقالة ويكيبيديا “Interface bloat” (المراجعة 1304469164)، والمتاحة بموجب رخصة CC BY-SA 4.0؛ تمت ترجمة النص وتحريره وإضافة أمثلة.