
على عكس العنوان الاستفزازي ، هذه ليست بنية جديدة ، ولكنها محاولة لترجمة ممارسات بسيطة ومختبرة بالوقت إلى Newspeak ، والتي يتحدث بها مجتمع Android الحديث
مقدمة
في الآونة الأخيرة ، أصبح من المؤلم أن ننظر إلى ما يحدث في عالم تطوير منصات الهواتف المحمولة. يزدهر رواد الفضاء المعماريون ، ويعتبر كل محب أنه من واجبه ابتكار بنية جديدة ، ولحل مهمة بسيطة ، بدلاً من خطين ، أدخل العديد من الأطر العصرية.
لقد ملأت هذه المواقع برامج تعليمية حول الأطر العصرية والبنيات المتطورة ، ولكن لا توجد حتى أفضل الممارسات لعملاء Android REST. على الرغم من أن هذه هي واحدة من حالات التطبيق الأكثر شيوعًا. أريد أن يذهب النهج العادي للتنمية إلى الجماهير أيضًا. لذلك ، أنا أكتب هذا المقال
لماذا الحلول الحالية سيئة؟
بشكل عام ، فإن مشكلة MVP الجديدة ، VIPER وما شابه هي نفسها تمامًا ، لا يعرف مؤلفوها كيفية التصميم. وأتباعهم - أكثر من ذلك. وبالتالي فهم لا يفهمون أشياء مهمة وواضحة. وهم منخرطون في
الهندسة التقليدية
الزائدة .
1. يجب أن تكون العمارة بسيطة
كلما كان ذلك أبسط كلما كان ذلك أفضل. وهذا يجعل الفهم أكثر سهولة وموثوقية ومرونة. يمكن لأي أحمق إعادة تعقيدها وتجميع مجموعة من التجريد ، ولكن للقيام بذلك ببساطة ، تحتاج إلى التفكير بعناية.
2. الإفراط في الهندسة أمر سيئ
تحتاج إلى إضافة مستوى جديد من التجريد فقط عندما لا يحل المستوى القديم المشاكل. بعد إضافة مستوى جديد ، يجب أن يصبح النظام
أسهل في الفهم ،
وأقل كودًا. على سبيل المثال ، إذا كان لديك بعد ذلك ثلاثة ملفات بدلاً من ملف واحد ، وأصبح النظام أكثر تعقيدًا ، فأنت أخطأت ، وإذا كنت بطريقة بسيطة ،
كتبت القمامة .
على سبيل المثال ، يكتب المعجبون بـ MVP في مقالاتهم بنص عادي أن MVP يؤدي بغباء إلى
مضاعفات كبيرة للنظام. ويبررون ذلك بحقيقة أنه
مرن للغاية
ويسهل صيانته . ولكن ، كما نعلم من الفقرة رقم 1 ، هذه أشياء متنافية.
الآن عن VIPER ، انظر فقط ، على سبيل المثال ، إلى الرسم التخطيطي من
هذه المقالة.
وهذا لكل شاشة! يؤلم عيني. أنا أتعاطف بشكل خاص مع أولئك الذين في العمل عليهم التعامل مع هذا ضد إرادتهم. بالنسبة لأولئك الذين قدموه بنفسي ، أتعاطف مع أسباب
مختلفة قليلاً .
نهج جديد
مرحبًا ، أريد أيضًا اسمًا عصريًا . لذلك ، تسمى العمارة المقترحة
RESS -
R equest ،
E vent ،
S creen ،
S torage. يتم تفصيل الحروف والأسماء بغباء شديد من أجل الحصول على كلمة قابلة للقراءة. حسنًا ، حتى لا تخلق ارتباكًا مع الأسماء المستخدمة بالفعل. حسنًا ، مع REST في اللحن.
إجراء حجز على الفور ، هذه الهندسة المعمارية مخصصة لعملاء REST. بالنسبة للأنواع الأخرى من التطبيقات ، ربما لن تعمل.

1. التخزين
مستودع البيانات (بعبارة أخرى نموذج ، مستودع). تقوم هذه الفئة بتخزين البيانات ومعالجتها (حفظ ، وتحميل ، والإضافة إلى قاعدة البيانات ، وما إلى ذلك) ، بالإضافة إلى جميع البيانات من خدمة REST لأول مرة هنا ، وتحليلها وتخزينها هنا.
2. الشاشة
شاشة التطبيق ، في حالة Android ، هذا هو نشاطك. بعبارات أخرى ، إنها ViewController عادية مثل MVC من Apple.
3. طلب
الفئة المسؤولة عن إرسال الطلبات إلى خدمة REST ، بالإضافة إلى تلقي الردود وإخطار استجابة مكونات النظام الأخرى.
4. الحدث
الارتباط بين المكونات الأخرى. على سبيل المثال ، يرسل الطلب حدثًا حول استجابة الخادم لأولئك المشتركين فيه. ويرسل التخزين حدثًا حول تغييرات البيانات.
فيما يلي مثال على تنفيذ مبسط. تم كتابة الرمز مع افتراضات ولم يتم التحقق منه ، لذلك قد تكون هناك أخطاء في الأخطاء اللغوية والأخطاء المطبعية
طلبpublic class Request { public interface RequestListener { default void onApiMethod1(Json answer) {} default void onApiMethod2(Json answer) {} } private static class RequestTask extends AsyncTask<Void, Void, String> { public RequestTask(String methodName) { this.methodName = methodName; } private String methodName; @Override protected String doInBackground(Void ... params) { URL url = new URL(Request.serverUrl + "/" + methodName); HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();
التخزين public class DataStorage { public interface DataListener { default void onData1Changed() {} default void onData2Changed() {} } private static MyObject1 myObject1 = null; private static List<MyObject2> myObjects2 = new ArrayList<>(); public static void registerListener(DataListener listener) { listeners.add(listener); } public static void unregisterListener(DataListener listener) { listeners.remove(listener); } public static User getMyObject1() { return myObject1; } public static List<MyObject2> getMyObjects2() { return myObjects2; } public static Request.RequestListener listener = new Request.RequestListener() { private T fromJson<T>(Json answer) {
الشاشة public class MyActivity extends Activity implements DataStorage.DataListener { private Button button1; private Button button2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); button1.setOnClickListener((View) -> { Request.apiMethod1(); }); button2.setOnClickListener((View) -> { Request.apiMethod2(); }); updateViews(); } @Override protected void onPause() { super.onPause(); DataStorage.unregisterListener(this); } @Override protected void onResume() { super.onResume(); DataStorage.registerListener(this); updateViews(); } private void updateViews() { updateView1(); updateView2(); } private void updateView1() { Object1 data = DataStorage.getObject1();
التطبيق public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); Request.registerListener(DataStorage.listener); } }
نفس shemka ، ولكن من حيث RESS ، لفهم يعمل مثل هذا: عند النقر فوق الزر ، فإن الطريقة المطلوبة للطلب هي الارتعاش ، يرسل الطلب طلبًا إلى الخادم ، ويعالج الاستجابة ويخطر DataStorage أولاً. يقوم DataStorage بتحليل الاستجابة وتخزين البيانات مؤقتًا في المنزل. يقوم الطلب بعد ذلك بإبلاغ الشاشة النشطة حاليًا ، وتأخذ الشاشة البيانات من DataStorage وتقوم بتحديث واجهة المستخدم.
علامات الشاشة وإلغاء الاشتراك من الرداءة في onResume و onPause ، على التوالي. كما يقوم بتحديث واجهة المستخدم بالإضافة إلى onResume. ماذا يعطي؟ تأتي الإشعارات فقط في النشاط النشط الحالي ، ولا توجد مشاكل في معالجة الطلب في الخلفية أو تحويل النشاط. سيتم تحديث النشاط دائمًا. لن يصل الإخطار إلى نشاط الخلفية ، وعند العودة إلى الحالة النشطة ، سيتم أخذ البيانات من DataStorage. ونتيجة لذلك ، لا توجد مشاكل عند تدوير الشاشة وإعادة إنشاء النشاط.
ولكل هذا ، فإن واجهة برمجة التطبيقات الافتراضية من Android SDK كافية.
أسئلة وأجوبة للنقد المستقبلي
1. ما هو الربح؟
البساطة الحقيقية والمرونة وقابلية الصيانة والتدرج والحد الأدنى من التبعيات. يمكنك دائمًا تعقيد جزء معين من النظام إذا كنت بحاجة إلى ذلك. الكثير من البيانات؟ كسر DataStorage بلطف إلى عدة. خدمة REST API ضخمة؟ قم بعمل طلب قليل. هل التسجيل بسيط للغاية ومحرج وغير عصري؟ خذ EventBus. هل تبحث عن محل حلاقة على HttpConnection؟ حسنا ، خذ التحديثية. نشاط جريء مع مجموعة من الأجزاء؟ فقط ضع في اعتبارك أن كل جزء هو شاشة ، أو قم بتقسيمه إلى فئات فرعية.
2. AsyncTask هو رجل سيئ ، خذ على الأقل التحديثية!
هاه؟ وما المشاكل التي تسببها في هذا الرمز؟ تسرب الذاكرة؟ لا ، هنا لا يقوم AsyncTask بتخزين ارتباطات التنشيط ، ولكن فقط ارتباط إلى طريقة ثابتة. ضاع الجواب؟ لا ، تأتي الإجابة دائمًا إلى DataStorage الثابت حتى يتم قتل التطبيق. هل تحاول تحديث النشاط عند الإيقاف المؤقت؟ لا ، تأتي الإشعارات في نشاط نشط فقط.
وكيف يمكن أن تساعد التحديثية هنا؟ فقط انظر
هنا . أخذ المؤلف RxJava و Retrofit وما زال ينحت العكازات لحل مشكلة لا تمتلكها RESS ببساطة.
3. الشاشة هي نفسها ViewController! تحتاج إلى فصل المنطق والعرض ، arrr!
إسقاط هذا الشعار بالفعل. يعد العميل النموذجي لخدمة REST عرضًا كبيرًا من جانب الخادم. كل منطق عملك هو تعيين الحالة الصحيحة للزر أو حقل النص. ماذا ستشارك هناك؟ لنفترض أنه سيكون من الأسهل الحفاظ عليه؟ الحفاظ على 3 ملفات مع 3 أطنان من التعليمات البرمجية ، بدلاً من ملف واحد مع 1 طن أسهل؟ حسنًا وإذا كان لدينا نشاط مع 5 أجزاء؟ هذا بالفعل 3 × (5 + 1) = 18 ملفًا.
الفصل في وحدة تحكم وعرض في مثل هذه الحالات ينتج ببساطة مجموعة من التعليمات البرمجية التي لا معنى لها ، حان الوقت للاعتراف بها. تعد إضافة الوظائف إلى مشروع باستخدام MVP أمرًا ممتعًا بشكل خاص: هل تريد إضافة معالج زر؟ حسنًا ، أصلح المقدم والنشاط وواجهة العرض. في RESS ، لهذا سأكتب سطرين من التعليمات البرمجية في ملف واحد.
ولكن في المشاريع الكبيرة ينمو ViewController بشكل رهيب؟ لذلك لم تر مشاريع كبيرة. عميل REST الخاص بك للموقع التالي الذي يحتوي على 5 آلاف سطر هو مشروع صغير ، و 5 آلاف سطر هناك فقط لأن هناك 5 فئات على كل شاشة. المشاريع الكبيرة حقًا على RESS مع أكثر من 100 شاشة والعديد من الفرق المكونة من 10 أشخاص تشعر بالارتياح. مجرد إجراء عدد قليل من الطلب والتخزين. وتحتوي الشاشة للشاشات الغامقة على شاشة إضافية لعناصر واجهة المستخدم الكبيرة ، على سبيل المثال ، نفس الأجزاء. سيغرق مشروع على MVP من نفس المقياس ببساطة في مجموعة من مقدمي العروض ، والواجهات ، والتنشيط ، والأجزاء ، والاتصالات غير الواضحة. والانتقال إلى VIPER سيجعل الفريق بأكمله يستقيل يومًا ما.
الخلاصة
آمل أن تشجع هذه المقالة العديد من المطورين على إعادة النظر في وجهات نظرهم حول الهندسة المعمارية ، وليس إنتاج ملخصات والنظر في حلول أبسط وأكثر اختبارًا للوقت.