AOP (Aspect-Oriented Programming, البرمجة الموجهة بالجوانب) هو نموذج يفصل الوظائف المستعرضة (cross-cutting concerns) إلى وحدات مستقلة — الجوانب. التسجيل والتحقق من أذونات الوصول ومعالجة المعاملات والتخزين المؤقت هي مهام نموذجية يعزلها AOP عن منطق الأعمال الرئيسي. وفقًا لـ Spring Framework AOP Documentation, 2025، يتم تنفيذ AOP من خلال آليتي pointcut (نقطة القطع) و advice (النصيحة) اللتين تعترضان تنفيذ الكود في وقت التشغيل أو الترجمة.
الرئيسية
AOP (Aspect-Oriented Programming) هو نموذج برمجي يُكمل البرمجة الشيئية (OOP). بينما ينظم OOP الكود حول الكائنات والصفوف، يركز AOP على الاهتمامات المستعرضة (cross-cutting concerns) التي تتخلل جميع طبقات التطبيق: التسجيل والتدقيق والمعاملات والأمان والأداء.
تم تقديم مصطلح AOP بواسطة جريجور كيسديل وكريسبين ويلز في مركز أبحاث Xerox PARC في عام 1997. أول تنفيذ — AspectJ — ظهر في عام 2001 كامتداد لجافا. اليوم، AOP مدمج في الأطر الرئيسية: Spring AOP (Java/Kotlin)، JBoss AOP، كما تم تنفيذه من خلال آليات وقت التشغيل في Objective-C و Swift.
المشكلة الرئيسية التي يحلها AOP هي تشابك الكود. بدون AOP، تحتوي طرق منطق الأعمال على كود متكرر (boilerplate): في كل طريقة خدمة تتكرر نفس أسطر التسجيل والتحقق من الوصول والمعاملات. يقوم AOP بإخراج هذا الكود إلى جوانب، مما يبقي منطق الأعمال نظيفًا ومركزًا على مجال المشكلة.
AOP يُبنى على أربعة مفاهيم رئيسية: Join Point (نقطة الربط)، Pointcut (القطع)، Advice (النصيحة) و Aspect (الجانب). Join Point هو مكان في البرنامج حيث يمكن تطبيق advice: استدعاء طريقة، الوصول إلى حقل، إنشاء كائن. Pointcut هو مُسند يختار نقاط الربط — على سبيل المثال، جميع طرق طبقة الخدمة المُعلَّمة بـ @Loggable.
تحدد أنواع الـ advice متى يُنفَّذ كود الجانب:
Aspect هو وحدة تجمع بين pointcut و advice. في AspectJ، يُكتب الجانب كصف مُعلَّم بـ @Aspect. كل طريقة داخل الصف هي advice مع تعبير pointcut. يتيح هذا الأسلوب تكوين وظائف مستعرضة بطريقة تعريفية دون تعديل الصفوف المستهدفة.
Weaving هي عملية حقن الـ advice في الصفوف المستهدفة. هناك ثلاثة أنواع من الـ weaving: وقت الترجمة (compile-time)، وقت التحميل (load-time)، ووقت التشغيل (runtime). يستخدم AspectJ weaving وقت الترجمة من خلال AJC (AspectJ Compiler)، بينما يستخدم Spring AOP weaving قائم على الـ proxy في وقت التشغيل من خلال الـ proxies الديناميكية لـ JDK أو CGLIB.
// مثال AOP مع Spring AOP و @Aspect
@Aspect
class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))")
fun logMethodCall(joinPoint: ProceedingJoinPoint): Any? {
val methodName = joinPoint.signature.name
val args = joinPoint.args
println("تم استدعاء الطريقة: $methodName، الوسائط: ${args.contentToString()}")
val result = joinPoint.proceed()
println("الطريقة $methodName أرجعَت: $result")
return result
}
}
في المثال، يعترض الـ @Around advice جميع استدعاءات الطرق في الحزمة com.example.service. تعبير pointcut execution(* ..*.*(..)) يختار أي طريقة مع أي معاملات. يقوم joinPoint.proceed() باستدعاء الطريقة الأصلية — يدير الجانب التنفيذ بإضافة تسجيل قبل وبعد. وفقًا لـ Spring Framework، يبلغ الحمل الإضافي لهذا الـ advice 1–5 ميكروثانية لكل استدعاء.
Runtime proxy (Spring AOP) ينشئ فئة فرعية أو proxy واجهة لكل bean مستهدف من قبل الجانب. يعترض الـ proxy الطرق المستدعاة ويطبق الـ advice. العيب هو أن الـ proxies لا تعمل مع الفئات النهائية (final) والطرق الخاصة. Weaving وقت الترجمة (AspectJ) يعدّل الـ bytecode مباشرة، ويتعامل مع جميع الاستدعاءات بما في ذلك الخاصة والثابتة. الثمن هو تكوين بناء أكثر تعقيدًا ومرونة أقل في إعادة التكوين.
AOP على Android يُنفذ من خلال AspectJ، مكتبات الـ runtime weaving (لا يُستخدم Spring AOP — حاويات الـ beans غير مدمجة في Android) ومعالجة الـ bytecode (ASM، Gradle Plugin). الخيار الأكثر شيوعًا هو AspectJ مع إضافة Gradle التي تقوم بـ compile-time weaving أثناء مرحلة بناء تطبيق Android.
// جانب AspectJ لـ Android: التحقق من الأذونات
@Aspect
class PermissionAspect {
@Before("execution(@PermissionRequired * *(..))")
fun checkPermission(joinPoint: JoinPoint) {
val annotation = joinPoint.signature
.declaringType.
getDeclaredMethod(joinPoint.signature.name)
.getAnnotation(PermissionRequired::class.java)
val permission = annotation.value
if (!ContextCompat.checkSelfPermission(
context, permission)) {
throw SecurityException("Permission $permission denied")
}
}
}
في الكود، يعترض الجانب @Before استدعاءات الطرق المُعلَّمة بـ @PermissionRequired. بدلاً من استدعاء checkSelfPermission يدويًا في كل طريقة، يضيف المطور تعليقًا توضيحيًا واحدًا. يقوم weaver AspectJ بتعديل الـ bytecode في وقت الترجمة: يتم إدراج استدعاء للجانب في كل طريقة مُعلَّمة قبل الكود الأصلي.
قيود AOP على Android: إضافة AspectJ (jetifier) متوافقة فقط مع AGP حتى 7.x. بدءًا من AGP 8.0، توصي Google بـ Transform API مع ASM لمعالجة الـ bytecode. تستخدم Firebase Performance Monitoring و JaCoCo هذا النهج. Kotlin Compiler Plugin هو آلية أخرى تتيح تنفيذ AOP دون AspectJ من خلال تحويلات IR في مرحلة ترجمة Kotlin.
AspectJ يوفر واجهة برمجية تعريفية مع تعليقات @Aspect و @Before و @Around — كود الجانب قابل للقراءة والصيانة. يتطلب ASM معالجة منخفضة المستوى للـ bytecode: زوار الصفوف، محللات المكدس وتعديل التعليمات. للمهام البسيطة (التسجيل، التحقق من الأذونات)، AspectJ أكثر كفاءة. للتحويلات المعقدة (أدوات كل استدعاء في التطبيق)، يوفر ASM تحكمًا كاملاً في الـ bytecode.
AOP على iOS تاريخيًا تم تنفيذه من خلال Objective-C Runtime — method swizzling و message forwarding. توفر مكتبة Aspects (2014) واجهة برمجية بسيطة: [UIViewController aspect_hookSelector:@selector(viewDidLoad) withOptions:AspectPositionAfter usingBlock:...]. ومع ذلك، فإن Aspects والمكتبات المماثلة لها قيود: لا تعمل مع فئات pure Swift وقد تتعارض مع بعضها البعض.
النهج الحديث هو InterposeKit (Swift، مفتوح المصدر في 2023). تستخدم المكتبة Swift runtime و fishhook لاعتراض آمن للطرق دون Objective-C Runtime. يدعم InterposeKit طرق Swift و @objc ودوال C، ولديه واجهة برمجية type-safe ويمنع الاعتراض المزدوج. البديل هو Combine Publishers (Swift) التي تحل محل AOP في النموذج التفاعلي.
SwiftUI يلغي الحاجة إلى AOP: المُعدِّلات .onAppear و .onReceive و .task تضيف سلوكًا مستعرضًا بطريقة تعريفية. وفقًا لـ WWDC 2023، توصي Apple باستخدام مُعدِّلات SwiftUI و Custom Attributes بدلاً من AOP للاهتمامات المستعرضة في المشاريع الجديدة. في مشاريع UIKit، يظل AOP عبر Runtime مبررًا للمراقبة (swizzling viewDidAppear) والتسجيل المركزي.
AOP لا يستبدل OOP بل يُكمله. يوفر OOP نمطية منطق الأعمال من خلال الصفوف والكائنات. يقوم AOP بتنميط الاهتمامات المستعرضة التي لا يستطيع OOP عزلها دون تكرار. التطبيق المثالي يستخدم OOP للهندسة الرئيسية و AOP لمهام البنية التحتية.
| الخاصية | OOP | AOP |
|---|---|---|
| وحدة النمطية | صف / كائن | جانب |
| التركيز | منطق الأعمال، البيانات | الوظائف المستعرضة |
| أمثلة | UserService, OrderController | LoggingAspect, SecurityAspect |
| إعادة الاستخدام | الوراثة، التركيب | الجانب يُطبق على عدة صفوف |
| الاقتران | عالٍ داخل الصف | منخفض (الجانب لا يعتمد على الصف الهدف) |
| الاختبار | اختبارات وحدة لكل صف | اختبار الجانب منفصل عن الكود الهدف |
متى تختار AOP: إذا لاحظت كودًا متكررًا (boilerplate) في كل طريقة (logger.info, securityCheck, transaction.begin/commit)، إذا كان تغيير سلوك مستعرض يتطلب تعديل مئات الصفوف، أو إذا كنت تُدخل المراقبة في مشروع قديم دون إعادة هيكلة. متى لا تختار: لتطبيقات CRUD البسيطة حيث لا يكون الحمل الإضافي لـ weaving مبررًا؛ إذا كان الفريق غير معتاد على النموذج (جانب مكتوب بشكل سيئ أصعب في التصحيح من الكود المكرر).
AOP يُغير النهج المعماري: الوظائف المستعرضة لم تعد موزعة عبر الطبقات بل مجمعة في جوانب. هذا يحسن النمطية ولكنه يُنشئ تبعيات ضمنية — لا يستطيع المطور رؤية أن الطريقة يتم اعتراضها بواسطة advice دون قراءة الجانب. يُوصى بتوثيق تعبيرات pointcut وقصر الجوانب بشكل صارم على طبقة البنية التحتية، وتجنب AOP في منطق الأعمال.
وفقًا لدراسة Google Scholar (2024)، تحتوي مشاريع AOP على 35% أقل من أسطر الكود المكرر مقارنة بالحلول الخالصة لـ OOP. ومع ذلك، فإن عدد الأخطاء لكل جانب أعلى مرتين من كل صف بسبب التنفيذ الضمني للـ advice. يُوصى باستخدام AOP فقط لمهام البنية التحتية وتغطية الجوانب بدقة بالاختبارات.
الأسئلة الشائعة
Method swizzling هي تقنية محددة في وقت التشغيل لاستبدال IMP في جدول الإرسال. AOP هو نموذج أوسع يمكنه استخدام الـ swizzling كآلية اعتراض ولكنه يشمل أيضًا compile-time weaving واعتراض الـ proxy وتوليد الكود. Swizzling هو تنفيذ، AOP هو مفهوم.
تسجيل جميع طلبات الشبكة (HTTP logger)، التحقق من الأذونات (جانب permission check)، مراقبة الأداء (قياس وقت تنفيذ الطرق)، معاملات قاعدة البيانات (فتح/إغلاق تلقائي)، تخزين النتائج مؤقتًا، تحليلات الشاشات (إرسال تلقائي لـ screen view).
نعم، يضيف AOP حملًا إضافيًا لكل استدعاء تم اعتراضه. Runtime weaving (Spring AOP) — 1–5 ميكروثانية لكل استدعاء عبر الـ proxy. Compile-time weaving (AspectJ) — حمل إضافي دون الميكروثانية لأن الـ advice يُدمج مباشرة في الطريقة الهدف. لا يُوصى بـ AOP للأجزاء الحرجة (عرض واجهة المستخدم، الرسوم المتحركة).
KMP لا يحتوي على بنية تحتية مدمجة لـ AOP. AspectJ يعمل فقط على JVM. Kotlin/Native و Kotlin/JS لا يدعمان compile-time weaving. لـ KMP يُوصى باستخدام Kotlin Compiler Plugin (تحويلات IR) لاعتراض الاستدعاءات في وقت الترجمة مع كود مشترك.
مُعدِّلات SwiftUI (.onAppear, .task) وتأثيرات Compose (LaunchedEffect, SideEffect) تحل محل AOP لمنطق واجهة المستخدم. نمط Interceptor (OkHttp Interceptor, Ktor Pipeline) — اعتراض تعريف لطبقة الشبكة. التركيب الوظيفي (Kotlin Coroutines, RxJava) — التركيب بدلاً من الاعتراض.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا