وقتی سفرم را به عنوان یک توسعهدهنده اندروید شروع کردم، کلمات «معماری برنامههای موبایل» باعث گیجی عمیق من شد، گوگل و مقالاتی در Habré من را به افسردگی شدیدتری سوق دادند - به کتاب نگاه میکنم و چیزی نمیبینم. فکر میکنم اگر در حال خواندن این مقاله هستید، قبلاً بیش از یک بار این تصویر را مطالعه کردهاید و سعی کردهاید بفهمید چه اتفاقی میافتد: به نظر من مشکل درک رویکرد معماری در توسعه تلفن همراه در انتزاعی بودن خود معماری نهفته است. هر توسعه دهنده دیدگاه خود را در مورد نحوه اجرای صحیح این یا آن الگو دارد. نمونه های کم و بیش مناسبی از پیاده سازی MVP در بخش انگلیسی زبان اینترنت یافت شد که جای تعجب نیست. بیایید به طور خلاصه به چیستی چیست نگاه کنیم و به یک مثال برویم. مدل - سطح داده. من دوست ندارم از اصطلاح "منطق تجاری" استفاده کنم، بنابراین در برنامه هایم آن را مخزن می نامم و با پایگاه داده و شبکه ارتباط برقرار می کند. نمایش - سطح نمایش. اگر رقصیدن با تنبور و تعامل با چرخه زندگی را دوست ندارید، فعالیت ، قطعه یا نمای سفارشی خواهد بود . اجازه دهید به شما یادآوری کنم که در ابتدا همه برنامه های اندرویدی تابع ساختار MVC هستند که در آن Controller یک Activity یا Fragment است . Presenter یک لایه بین View و Model است. View رویدادهایی را که برای آن اتفاق می افتد منتقل می کند، ارائه دهنده آنها را پردازش می کند، در صورت لزوم به Model دسترسی پیدا می کند و داده ها را برای رندر به View برمی گرداند. در رابطه با اندروید و یک مثال خاص، قسمت مهم - قرارداد را برجسته می کنم. این رابطی است که تمام تعاملات بین اجزای فوق را توصیف می کند. به طور خلاصه بخش نظری:
- مشاهده درباره ارائه کننده می داند.
- ارائه دهنده در مورد View و Model (Repository) می داند.
- مدل به خودی خود؛
- قرارداد بر تعاملات بین آنها حاکم است.
MainContract
:
public interface MainContract {
interface View {
void showText();
}
interface Presenter {
void onButtonWasClicked();
void onDestroy();
}
interface Repository {
String loadMessage();
}
}
در حال حاضر، ما به سادگی 3 مؤلفه برنامه آینده خود و کارهایی که آنها انجام خواهند داد را برجسته می کنیم. در ادامه به شرح مخزن خواهیم پرداخت:
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()");
}
}
یادت هست در مورد رقص با تنبور و چرخه زندگی نوشتم؟ ارائهدهنده تا زمانی که View آن زنده است، در هنگام توسعه سناریوهای پیچیده کاربر، به شما توصیه میکنم که همه تماسهای View را در ارائهدهنده کپی کنید و در لحظههای مناسب با آنها تماس بگیرید و چرخه حیات Activity/Fragment را کپی کنید تا به موقع بفهمید چه چیزی چیست. باید با داده هایی که در حال حاضر در "بین الیه" آویزان است انجام شود. و در نهایت مشاهده کنید:
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()");
}
}
چه خبر است؟
- Activity که به عنوان View نیز شناخته می شود،
onCreate()
یک نمونه Presenter را در یک متد ایجاد می کند و خود را به سازنده آن منتقل می کند. - هنگامی که یک ارائه کننده ایجاد می شود، به صراحت یک View دریافت می کند و یک نمونه مخزن ایجاد می کند (به هر حال، می توان آن را یک Singleton ساخت)
- وقتی دکمه ای فشار داده می شود، View به ارائه کننده ضربه می زند و می گوید: "دکمه فشرده شد."
- ارائه دهنده به سمت مخزن می رود: "این مزخرف را برای من دانلود کنید."
- Repository "موارد" را بارگیری می کند و به ارائه کننده تحویل می دهد.
- ارائهدهنده به View روی میآورد: «اینها دادهها برای شما هستند، آنها را بکشید»
GO TO FULL VERSION