عندما بدأت رحلتي كمطور لنظام Android، تسببت عبارة "هندسة تطبيقات الهاتف المحمول" في حيرة شديدة، ودفعني Google والمقالات عن حبري إلى مزيد من الاكتئاب - أنظر إلى الكتاب ولا أرى شيئًا. أعتقد أنك إذا كنت تقرأ هذا المقال، فقد سبق لك أن درست هذه الصورة أكثر من مرة وحاولت فهم ما يحدث: إن مشكلة فهم النهج المعماري في تطوير الهاتف المحمول، في رأيي، تكمن في تجريد الهندسة المعمارية نفسها. كل مطور لديه رؤيته الخاصة لكيفية تنفيذ هذا النمط أو ذاك بشكل صحيح. تم العثور على أمثلة أكثر أو أقل لائقة لتنفيذ MVP في قطاع الإنترنت الناطق باللغة الإنجليزية، وهو ليس مفاجئا. دعونا نلقي نظرة سريعة على ما هو وماذا وننتقل إلى مثال. النموذج - مستوى البيانات. لا أحب استخدام مصطلح "منطق الأعمال"، لذلك أسميه في تطبيقاتي المستودع وهو يتواصل مع قاعدة البيانات والشبكة. عرض - مستوى العرض. سيكون نشاطًا أو جزءًا أو عرضًا مخصصًا إذا كنت لا تحب الرقص بالدف والتفاعل مع دورة الحياة. اسمحوا لي أن أذكرك أنه في البداية تخضع جميع تطبيقات Android لبنية MVC ، حيث تكون وحدة التحكم عبارة عن نشاط أو جزء . المقدم عبارة عن طبقة بين العرض والنموذج. ينقل العرض الأحداث التي تحدث له، ويقوم المقدم بمعالجتها، إذا لزم الأمر، ويصل إلى النموذج ويعيد البيانات إلى العرض لعرضها. فيما يتعلق بنظام Android ومثال محدد، سأسلط الضوء على الجزء المهم - العقد. هذه هي الواجهة التي تصف جميع التفاعلات بين المكونات المذكورة أعلاه. تلخيص الجزء النظري:
- عرض يعرف عن مقدم العرض؛
- يعرف مقدم العرض عن العرض والنموذج (المستودع)؛
- نموذج في حد ذاته؛
- العقد يحكم التفاعلات بينهما.
MainContract
:
public interface MainContract {
interface View {
void showText();
}
interface Presenter {
void onButtonWasClicked();
void onDestroy();
}
interface Repository {
String loadMessage();
}
}
في الوقت الحالي، نحن ببساطة نسلط الضوء على المكونات الثلاثة لتطبيقنا المستقبلي وما سيفعلونه. بعد ذلك سوف نقوم بوصف المستودع:
public class MainRepository implements MainContract.Repository {
private static final String TAG = "MainRepository";
@Override
public String loadMessage() {
Log.d(TAG, "loadMessage()");
/** Здесь обращаемся к БД or сети.
* Я специально ничего не пишу, чтобы не загромождать пример
* DBHelper'ами и прочими не относяшимеся к теме an objectми.
* Поэтому я буду возвращать строку Сосисочная =)
*/
return "Сосисочная у Лёхи»;
}
}
كل شيء واضح معها، فقط تحميل وتفريغ البيانات. التالي هو المقدم:
public class MainPresenter implements MainContract.Presenter {
private static final String TAG = "MainPresenter";
//Компоненты MVP applications
private MainContract.View mView;
private MainContract.Repository mRepository;
//Сообщение
private String message;
//Обрати внимание на аргументы конструктора - мы передаем экземпляр View, а Repository просто создаём конструктором.
public MainPresenter(MainContract.View mView) {
this.mView = mView;
this.mRepository = new MainRepository();
Log.d(TAG, "Constructor");
}
//View сообщает, что кнопка была нажата
@Override
public void onButtonWasClicked() {
message = mRepository.loadMessage();
mView.showText(message);
Log.d(TAG, "onButtonWasClicked()");
}
@Override
public void onDestroy() {
/**
* Если бы мы работали например с RxJava, в этом классе стоило бы отписываться от подписок
* Кроме того, при работе с другими методами асинхронного андроида,здесь мы боремся с утечкой контекста
*/
Log.d(TAG, "onDestroy()");
}
}
هل تتذكر أنني كتبت عن الرقص بالدف ودورة الحياة؟ يعيش المقدم طالما أن العرض موجود، عند تطوير سيناريوهات المستخدم المعقدة، أنصحك بتكرار جميع عمليات رد الاتصال في المقدم واستدعاءها في اللحظات المناسبة، وتكرار دورة حياة النشاط/الجزء، من أجل فهم ما هو في الوقت المناسب يجب القيام به مع البيانات المعلقة حاليًا في "الطبقة البينية". وأخيرًا شاهد:
public class MainActivity extends AppCompatActivity implements MainContract.View {
private static final String TAG = "MainActivity";
private MainContract.Presenter mPresenter;
private Button mButton;
private TextView myTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Создаём Presenter и в аргументе передаём ему this - эта Activity расширяет интерфейс MainContract.View
mPresenter = new MainPresenter(this);
myTv = (TextView) findViewById(R.id.text_view);
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.onButtonWasClicked();
}
});
Log.d(TAG, "onCreate()");
}
@Override
public void showText(String message) {
myTv.setText(message);
Log.d(TAG, "showMessage()");
}
//Вызываем у Presenter метод onDestroy, чтобы избежать утечек контекста и прочих неприятностей.
@Override
public void onDestroy() {
super.onDestroy();
mPresenter.onDestroy();
Log.d(TAG, "onDestroy()");
}
}
ماذا يحدث هنا؟
- يقوم النشاط، المعروف أيضًا باسم العرض،
onCreate()
بإنشاء مثيل Presenter في إحدى الطرق ويمرر نفسه إلى منشئه. - عندما يتم إنشاء مقدم العرض، فإنه يتلقى عرضًا بشكل صريح ويقوم بإنشاء مثيل مستودع (بالمناسبة، يمكن جعله مفردًا)
- عند الضغط على زر، يقرع العرض على مقدم العرض ويقول: "تم الضغط على الزر".
- يتحول مقدم العرض إلى المستودع: "قم بتنزيل هذا الهراء من أجلي."
- يقوم المستودع بتحميل "الأشياء" وتسليمها إلى المقدم.
- يتحول مقدم العرض إلى العرض: "هذه هي البيانات، ارسمها"
GO TO FULL VERSION