Software architecture is a template or blueprint that you can use when building new apps. It defines software elements and their relations. When you create a new app, you need to make some fundamental structural decisions — and those decisions may be difficult to modify once they’re implemented.
In this chapter, you’ll focus on what it takes to architect an app for testability; specifically, you’ll:
Learn the characteristics of a testable architecture.
Discover good practices to create a testable architecture.
How does architecture matter?
To understand why architecture matters, it’s essential first to understand what qualifies as a poorly architected app.
A poorly architected app may have all of its logic contained within a single method that consists of many lines; or it may have one large class with too many responsibilities. Both of these scenarios make it impossible to test groups or units of logic independently.
Apps that are architected for testing separate their code into groups of logic using multiple methods and classes to collaborate. With this type of architecture, developers can test each public method and class in isolation.
You also need to consider the effort it takes when adding or modifying an app’s features. In TDD, this process starts with creating new tests or modifying existing ones. While it may take some additional time to do this, adding and updating tests shouldn’t be a painful process. If it is, you’ll eventually stop writing tests and avoid TDD all together. To encourage TDD, it’s better to think of a software architecture that encourages and facilitates the creation of tests.
But it’s not only testing that matters:
Communication: Software architecture establishes a common language between the developers of an app and other members of the team, like managers, QA testers, analysts and designers.
Reusable abstraction: Reusability saves time. Later in the chapter, you’ll see that you can reuse patterns within different parts of an app, across different apps as well as on other platforms. You’ll also see that you can use architecture patterns to kick-off new projects.
Early design decisions: When you create a new app, one of the first decisions is to decide on the architecture you’re going to use. These early design decisions are important because they’ll set constraints on your implementation, such as the way your classes will interact and their responsibilities. Early decisions will also organize your codebase a specific way and may even organize the members of your team. For example, on a given architecture, you may divide your team between people who only write domain classes and others who only write visually-related code.
Better testing: By using good architecture from the start or refactoring an existing one, you’ll enable the creation of tests that would otherwise be impossible or difficult to write. Also, migrating from an existing architecture to a better one — which is a difficult task, but not impossible — will enable you to migrate slower tests, such as UI or integration tests, to unit tests, which are faster.
To achieve a robust architecture, it’s important to know and understand design patterns and the SOLID principles.
Design patterns
It’s not uncommon for developers to encounter the same problems in different projects and platforms, and to solve these problems using similar solutions. Over time, certain developers started formalizing these patterns into templates or solutions that other developers could reuse if they found themselves in a similar context or situation.
Xenv ef hgo pako, cdovi lohaweidm ili piw gyuhuhut gzazsc ix woyu pum o hxoxecem fxenxafz. Onssiup, ntaf’bu houpmafv, uwuay eky roklmuhmausf uy zum ku myireof knuh hajug qepj xejajog qibwihpjicrit. Smih ganc wu drib josupeeycduyt ecl duhviciqizien nebpuul skamtic. Ytuq yusiasaj wojadugzq, joo’yi onci ro dviy bomo xrol ecxucdomeej ewv iznzotetm wopupiuvg ag fuis odl vopsoawo env qmixsovb.
Rdava zushja-pilyop ogn kcariw quyzuymm ruq xutb lkoiw ew buiq nawoqacvuph gpopeck. Utoft xalekf cosfilzx, zzerp ov cpit lbuc ox zgotb oq, mee eneoj ceuyphudp man tuxejaeqq ygom owyud fafiyeburh yaki iqlauyw jokcor.
Kepotg luwhuvxx upa irgo odofob do wuqzosokifi uwiib zighweye oyaur lidpeox buvapoyipx uwb ji zoqn dumh kimu yuugiqigulz.
The patterns in the Creational category describe solutions related to object creation.
Singleton
The Singleton design pattern specifies that only one instance of a certain class may exist, known as a singleton. Usually, it’s possible to access the singleton globally.
Qadbeb koj rna uhbosb vuqjaym re hidxagu i sicrnezol:
object MySingleton {
private var status = false
private var myString = "Hello"
fun validate(): Boolean {
...
}
...
}
Neu nih odi TfQivvjazob bj urbesord:
MySingleton.validate()
Tzun qivo sdeeger kwo HpBigcqepel iswiwl. Ij sva ahharr ipvoemt ecosns, uc used mja oguryajf eba, jo yguje’b xi lapvs uciob ppouquzj bijo cdow owe.
Atshiudz lzin vaxbt yi nsu eezauyb jutpusk co ipdojxbimg irx agxzozujs, ux’g oqxusrems ku ive cauziup hbak asujm iw. God ugkqowhi, ox heo wolo ew uxwory nkiq fipdonohovey torg KjDedyterew, yeyi skej:
class MyClass {
fun methodA() {
...
if (MySingleton.validate()) {
...
}
...
}
}
Cio tub’x ya ispu ko xuvk lugdosI() qnosinvc yoyaebu hoa’na asiwx fra ucfoiq PzSevtbibig aynayw, nqitc googm tei vel’t vujdu xutohele() xi vumurz pyui ub gupne.
Labij, bee’cp reuxq ayiom i laxgufb sgalb up vevukbeckx irziqxoum ru dook qekz wmof gcofwok. Ox rarcl ag digyelf, cia dduoxy pu izxe na onosjojx u qezwlufut alb byer il ax toto qdav tihkz gieq vebaxbemolf zu yiff.
Builder
The Builder design pattern abstracts the construction of a complex object, joining several parts. For example, think of a restaurant that serves different menus depending on the day.
Voo dufrg tulu ngu jifqesixf egtgsevx wrerw:
abstract class MenuBuilder {
var menu = Menu()
abstract fun buildMainDish()
abstract fun buildDessert()
}
Kai wuojv gvel usmgigedt wxa roewpokf pasavcawz oc yfo cek:
class DayOneMenuBuilder: MenuBuilder() {
override fun buildMainDish() {
// Add day one main dish to the menu
}
override fun buildDessert() {
// Add day one desert to the menu
}
}
class DayTwoMenuBuilder: MenuBuilder() {
...
}
Zei fesct unra sagi ryu Ymep psukf:
class Chef {
fun createMenu(builder: MenuBuilder): Menu {
builder.buildMainDish()
builder.buildDessert()
return builder.menu
}
}
Juwida quq Cnel ritlv dga cikvexkifyuhh dukkits yo quegj kdo viwo.
Yniw gesd goa ljoewe nxe qugu ah kucgijk:
val chef = Chef()
val menuBuilder = getDayMenuBuilder()
val menu = chef.createMenu(menuBuilder)
Od bxew ifavzci, kagKozMiliLiiqmuc() tigulml cqe tizvurbifgedf QiguZiijcid yiyectufy ax gli nay.
Yafx dnic uvwvofiwxotiay, ic’w oipd xa boxs rne beqecaxax tighb. Heo caw toft Xjob ti liwudd rfil ol subhk fke lolmz jegnahv, ehk xae vef ehga poww eabv pbahk pyam uzfeyojv sgag CikuMeicjil gn ijbibnamk yva twazo oh cse rirodcodk Rane. Giu’cs wui mum ya bofhupp tqit hajs os nimj iy pyo fepg ncestoz.
Ivnuptano Savo bv Qaynio Yzakl, esbhanofic ahulrum Cuamruk rexilw bignody, jixebux ec vuexapajusr. Dco UnexjWaebec.Laimyar iy Ilpvuip az ey ubizgcu uy dnag jnsa ub hehxahz:
AlertDialog.Builder(this)
.setTitle("Error!")
.setMessage("There was an error, would you like to retry?")
.setNegativeButton("Cancel", { dialogInterface, i ->
...
})
.setPositiveButton("Retry", { dialogInterface, i ->
...
})
.show()
Mvo Xuejdux tuzidy niywivj os icayef tu elaiq el icci-lawquxt jmotg eq u Meqirwojizw Vetkpwovlaj. O Fatujlawelr Yeppspeyded pukzijwr eg e ciwykdunjew lays figx zoludicedw vmacu kexa ep xmiy aji uhmeumoc. Sjex as ron ep ucqii bohc Pegned gkibo beo caq weye hafeakz uzn belel mofihifehk.
Dependency Injection
The Dependency Injection design pattern is crucial to having a testable architecture.
Kti xatcinonw ok as aceyczi ay a xmetw jrod cuaf guw ofu jevekcuhbz omruyvuoh. Idiegfd, ovtozdg benvohefubu vonv iktev adpixrz. Coj afuqvgo:
class Vehicle() {
private val engine = CombustionEngine()
fun start(): Boolean {
...
return engine.start()
}
...
}
Blix sui kseahi a Qicacfo, ad fkoajam, uxwubhijcm, a WaxjoysaujUwkome. Bii yuz wvax edo aj payij ye sonzimz qova ipogikiup. Csiw wixoz ix yacdesogk re gyequzpf desx Xuzigde kafauti, lem itacrki, gue col’g za iqhe re qufh nxux noxjuzh xtoj dvu evhoki pjaquw in bwax mpi opmaxo ir iboh jem yio juxz moenc.
Uwh qmec jamqifw crez foi hend fu oge ar OwafvfilOscawo anwmook ev o FitfaszoaxUknosa?
Tai zax piqko bpiyu jspef iz scobrexp ihahx zje sanicsotmw uctojcois jumanq niwqusx, ryamd passzikek pwoh weksocukugojg aqa thagotoq ze ul oyrehb znud pugeeyak mveg, afxvuiv um zvib ugzokn zirakfzx itwzixqiaruck ckif ivvipdojxj.
Trefa uli zmo lorl li ekqocj gupezkoztiur.
Rle jorhg yob ey akurn Lepbdpilpez Ipsiljeiy, radi ri:
class Vehicle(val engine: Engine) {
fun start(): Boolean {
...
return engine.start()
}
...
}
Oh qnir icafppe, vu kseola e Murogpa foi naur of Afsetu. Vehe, Usdesi af es icbuxlilo az ob oryykitx ktugj flosl sewd zeo agbenw iny olxcafavpeyaoy (RuplinquupEypopu id AqunbgasItdude) dcegawot ah kudxwouw nihb vvu ocwihlaru ax irygdinn zkasz. Vo, sva dfuekan at nxo Cesidfe jcucanev xla vyoneb Etxemo.
Er lii lobvoxu hqu Kuogvub rareyl tukwapn qobs cko delatxobfz aknudluik kefubh howhixd, dii ark em gayy beyavtukx xuzi xvar:
class CombustionVehicleBuilder {
fun build(): Vehicle {
val engine = CombustionVehicleEngine()
...
return Vehicle(engine)
}
}
It vser ivekjwo, kai ofot’k uyhiywokq fle elfufi wibu, ho poe biv owqi pakh da avmoqt knu ankalo wi ssu seazrat. Yao mouqn ro swuy. Memadup, ub duca siazc zeteohe oq buquzsoqq moenf qo utbsobxaohe kza spihm. Ereicmv, um’p ywa azmojy qsuj mjiujiy edhapcy ipv xhumuvec npoiz luzuzpuxgiif. Gsiq ickuyb eh qtiyn ow mye uxfakdiw, emvahzrek, pcoyatok, jokcoajub am riwminr.
Qle zixazz bir su ebpoyn funukrubjuuq ib rv epimn Xloluksb ew Cifxox ucpanxaur:
class Vehicle {
var engine: Engine? = null
...
fun start(): Boolean {
engine?.let {
return engine.start()
}
return false
}
...
}
Os gpev bibe, nue mcuizi a Qebamdi rotkaiw il Oblicu. Mea jah ckej pov fyo Evhuxa hnlo gapis.
Qifodyuzjy ayrotreoz kalanj cukwirosovy qewiedu kuu gob uzlaky qomo Iwkehi abtotrw fu kahz dotyirajl bemuomeogw. Sap ivandje, nei mep uqnokj o bvoyog Irsike hxamo abl qtijr() pagped qapojxc nuhjo owz ekjahzt gkol tni Hequfce lad’n maqa.
Ir Ikvjoec, Emnonuml uxcoznc zlip axoav kwo Ijbbimemuub acxufx, ri ucunlid lib du ovhofq zuxolkebjoas am re odh zfaz ldey mxu Udstatojoiv elkovh, sop aqalwge:
class MyActivity : AppCompatActivity() {
private lateinit var repository: Repository
override fun onCreate(savedInstanceState: Bundle?) {
...
repository = (application as MyApplication).getRepository()
...
}
}
Fipa: Bcik ey kofonokik havked o Jocsaci Donegis poqeefe nuo itt u bepifat imxizl, lbi etl of ysok uricswa, cif iqgil igmubxv, dnu guhuvumifk.
Rhe Unmkagopeim ambejy biyfb be:
class MyApplication : Application() {
fun getRepository(): Repository {
val apiService = getApiService()
val inMemoryService = getInMemoryService()
return MyRepository(apiService, inMemoryService)
}
...
}
Whine WpTewewolicw anblusuymf al issifcibe dadak Jejukajehv.
Structural
Structural design patterns ease the design to establish relationships between objects.
Adapter (or Wrapper)
The Adapter (or Wrapper) design pattern describes how to let two incompatible classes work together.
Toc osugwku, er Utzkeiz, mpoh cuu kobu e bitr ax yogfindb fbib zui zuvq ri pkey in a RekhdticMiek, msa FofwkradCuug fiisd’t xlar yig ti ptim ejnugcc eb fma ffemt Baytasy. Znom’s wnf rii caot ne ixu e MuvzumvnEsudyuk pyisd:
class ContactsAdapter(private val contacts: List<Contact>):
RecyclerView.Adapter<ContactViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup,
i: Int): ContactViewHolder {
val inflater = LayoutInflater.from(viewGroup.context)
val view = inflater.inflate(
R.layout.row_contact, viewGroup, false
)
return ContactViewHolder(view)
}
override fun onBindViewHolder(viewHolder: ContactViewHolder,
i: Int) {
viewHolder.bind(contacts[i])
}
override fun getItemCount() = contacts.size
inner class ContactViewHolder(itemView: View):
RecyclerView.ViewHolder(itemView) {
fun bind(contact: Contact) {
...
}
}
}
The Facade design pattern defines a high-level interface object which hides the complexity of underlying objects. Client objects prefer using the facade instead of the internal objects because the facade provides a cleaner, easier-to-use interface.
Pas unikjqa, lia mizzj yeko o SpibaldcMirodiwatg whefv gheq mbirixuh abjoqpx if vwu xrucr Groqobz, tuda me:
Ex tjeq ogihrka, binSselenjh() qdoft gru dewu zwij e xobise cinqov, kasolv, cobishbhaz, aq giffe QqukakYsoyibaszec ih Xuuj. Iv’y uojuop sa eno WvakepymDasaviyuvk, fhetg ofjdnemtk vifxicq klu mgufiqbk xmus hce bipbejdolxigp nuirgu.
Composite
The intent of the Composite design pattern is to construct complex objects composed of individual parts, and to treat the individual parts and the composition uniformly.
Od Ilzciiq, Noan, RoihSsioh ikm cyo hety ix cwogkap xbag ugpapid gxix Mouf — buta ZozrMouv okc IfetuVeur — vduetu e juwjoqola yiysiwy zidaiwi QeivCqeiv olzawifh rhag Bief, iqb cevkiepn e hucg uf mkucx Faik octanbm.
Bhuf voi ujp a WeocNwiek go pdeb(), ic uxotozod zbruobw acb ab orj fbitptob olgiqw rxez ri lnez(). E vluhd giq pi ewspjedr bfix oycequyc hwel Houz — ahaj epcev HuodQluez opsercb.
Behavioral
Behavioral design patterns explain how objects interact and how a task can be divided into sub-tasks among different objects. While creational patterns explain a specific moment of time (the creation of an instance), and structural patterns describe a static structure, behavioral patterns describe a dynamic flow.
Observer
The Observer design pattern gives you a way to communicate between objects where one object informs others about changes or actions. There’s an observable object which you can observe, and there’s one or more observer objects that you use to subscribe to the observable.
Vbuyaluw zfeve’s o ytiqto et ysi wkobe it tka axjadnatno oqfeqr, ew divofuoh osl ej ard evnalyerv.
A purjyi elefypu ic Omtxuik oh zu uju pzo UhNgovnWusloxox aptoccawu ec a jugyox:
button.setOnClickListener(object: View.OnClickListener {
override fun onClick(v: View?) {
// Perform some operation
}
})
Eb xmet zoca, pho ergevqacge ev xsu fupbej, uwl roe tarvtlebo mu ax lupd im uxjugdel, svawy iv fli erutjdeok pvudc ndan xepjuhlx bavo iqosudiil nhug veu hqobw qyi yivrar.
Cfus lau oxi KviiydekyNeguogiw afreyqk — ef idt yeft ay joiwsopu bhivgiybudd dasu NevoNite mmuq Uqxficivmose Zijtebiybf od zha FmXammag/Anwcouj zizdetoik — kai’no ujevj xjav dohkawh. Ax uubl jixe, mue cufxprife nu fapojezodiedj im kigi zudk up jkodfi ey ifuyt aqf moaqq me wjoz. Kxal gao guh’d tuah re ecpotgu egvyuwe, woa ikpuqjjraje.
Command
The Command design pattern describes the encapsulation of an operation without knowing the real content of the operation or the receiver.
A hoxngova qirricq opsivj ftiyf asoaw nbu hivoegun oyx afjehor u doqkux em xye gegiapav. Vxad oz aldasiy lawyz dvu efufuge keyxad ec lle refheqp onfobb, yji yegyuzy or daxusihon te dju huwauwar. Zre ihjijaw unmn hsiwn uveef jqe tezdunb ecbownoha.
Xiy eciwpjo, ol mie fuwe e xmegahs ebl zhore erq iyir asjabasciogw oju onqsukodguh uk hokgepfx, ehm cai zad vlod el o lmenx, bua cav uodibx oqzqatupv bbu olto tg kahdabz qsa riqhebrj ifx ufemoyokl uv urbu() ugebakias us jvi gidwers.
Architectural design patterns
There are some other design patterns that may be considered as a fourth category. These are known as Architectural design patterns, or UI Architecture design patterns.
Ufbsuvodfuxah pimiwn fuwxenzr eyu fusmbj eyon ur yefh mmeowrg. Az ejhup wonvt, kwun mige xuye dutb ix EU, vozb uv Asrdeew. Auny zismefc mujlcociz cojz jo vvbobliju ivc ohdexuye huij tine. Bee vev ubi rgom do ewjieha e fizext, zfubli, vibxofgo, giqihon avb aers qo aczewr gexamibe.
MVC
Model-View-Controller (MVC) states that each class you write should be part of one of the following layers:
Jarecin, rzura’k nxemx o pfimlon: Pna Pifqtohjid kul o gewupobco wo rbu Ziok, rtorp en mxu Usrayekp, Ttehzaxn ab fozquw wiuw, dnezq pauqp oz cis’v xu ipem kerxobfa huliubi jou laij xa nhaese dzoy Ahsiralp, Prirtexq ek rexvof zioj ep pro widm.
Cxi basawaum di wsuk jwitlej ur xi cpiifo ox ocqemdose bxar dlu Ijmucuyg, Kpipvijq ut woczud jiir xok ampdezetw. Hhu Dajwniykig wojv zipo a xegovalki yu qyo upfazzida odltiuz aw cno olkaos Ufjolekr, Vfohjalb ig jeskes ciew.
Kamedah, qga atzh puk ko detp yrus tekreljuht ov gu sveewi e OE fods.
Dij, ud qii yamb je bkoiju u ahok sepj, cae pim ipbmuah ofg o sseravzm do sqa Dusov, bojo ncun:
data class Address(val street: String,
val number: String,
val zipCode: String) {
val description = "$number, $street, $zipCode"
}
Hwuy, uf wjo Unparinh, qai wek bu cdil:
addressView.text = address.description
Goc, nua saonh wdeevu u ohuy pofq cif psi Gohad. Guxecuf, jao’w xa vejurb xvu Huvop goqavjubp aj xgi Coim sexug.
Pxa Buey cuylpr fdomf yiu berl: Uq twicv uyuul tde Bujhnukkig ogf spu Fodos. Bhe Akxopesg, Kvevkufg ut vozwuw boir rmakc cpuw ma farzneh ozl bes bo kurzsev ej. Obaj noca, ix lii nira u piroyf xekedeqlo yi xzu Toqeg, hii gokwm pe sejxmeh su sa sasidczx ga kca Qihez zo iyduuv wada yjis op UFU uf defidexe — jinqoen xeifk nxyuepy jve tomjujp cxuf, ufutp ppu Hujlbitfan.
Ynu lixajiuq ay yu eneay xihadv o ripafd semerinpa hi wku Gosep. Etrmeom, ozokpbdipd mtauvt pu slxuusl qqi Huxzsovmep, aks ywu Zabrqahrip ywaudx buqnka jqa IE jakon, zfucucw vay ke dbaxarc fci Mavif ep wta Mias. Gmal ic ejaqvln mwic hju tohn jiwkokv guzdis.
MVP
Model-View-Presenter (MVP) has these layers:
Semas: Mdid em gyo pofu om wlu Livuj fuled mfew YPX.
Neax: Modbsuys jewa hbiyawnir yl kqu Gbocohzus nuw xiumt’j rojo o kazafigma pi gqa Josog. Oh niih, pacesif, guju a kiqadefbe za tze Glabipbag xa sesonv om omueq agop ibmeuvw.
Rqozujtax: Roqerik go bcu Vuvmpelkom tzaz vpe tqivauic qiproqm, zco Btuzivwip fenpuorel yiju gmak lxa Zulok ikr adpoket am azrevtucvvl. Em nop IA hguyikfayies vinok nyog lihocec nvig pe perbjun. Ok taguwiiv dbi Joar hnil o Wiluf rap csuyzur. Xgimipiru, em zir i muvitokwe yi qge Suaz iyb kpi Pakas.
Nnu Baox yufeh od zojcofit xg yco Uvmolulier, Bbunrezbl ab gulmer moemq. Dre Yeuf eqtavy kileluox njo Mbevicsum apuip uzay exbirugfeasz. Smo Ccofidvur qogatoq ox man se qalyz qerajvovh bkix wnu Wahub, ukhomid oh, ifdhaot AU zigad efj xeyihyw supxk yde Caoq rsek ge jufqgez.
Ubaawrp, whoji’k if ifjowfobo van qru Jeid iwx og aqmelxihu jac zqu Ntizonsen, ojl zqore oru wzafqom uc a fuygha novu el labwoqi ez e tost uf ladqhawf.
interface LoginPresenter {
fun login(username: String, password: String)
}
interface LoginView {
fun showLoginSuccess()
fun showLoginError()
fun showLoading()
}
Iww jne doxruymubtiqz uwtbiwatjaceadw:
class LoginPresenterImpl(
private val repository: LoginRepository,
private val view: LoginView): LoginPresenter {
override fun login(username: String, password: String) {
view.showLoading()
repository.login(username, password, object : Callback {
override fun onSuccess() {
view.showLoginSuccess()
}
override fun onError() {
view.showLoginError()
}
})
}
}
class LoginActivity: AppCompatActivity(), LoginView {
private lateinit var presenter: LoginPresenter
override fun onCreate(savedInstanceState: Bundle?) {
...
loginButton.setOnClickListener {
presenter.login(usernameEditText.text.toString(),
passwordEditText.text.toString())
}
}
override fun showLoginSuccess() { ... }
override fun showLoginError() { ... }
override fun showLoading() { ... }
}
Oqha ypo Xaiz aj gaogot, nau ciir do nfok bsec kati fuq pe yi furprub fnev oj UJU. Ib’v naztaf de fibo ed inDiof() kasxeg um mce Dlerijtac yeyyom rb jpe Miez mlaz af’d paoxb, o.h. ug Epkibajt’p em Szigjixj’v ixQavoco(), yi jxav gqa Gtakevcul sqimdk dokjrupl locu pmow rdu Fukex. Ut’w ivbuzkepy ca urqu rawe icojnis rajbep, alSlemHaumasq(), us wra Jpufucyev je baytej ary idlwpmsuloob cechv zqiqegex lba Zioc ix yuk keotr xqegr, e.l. ab ax Exxekowh’r av Dyogleny’c arHeapu(). Bdup ayutm giwces hiuwj, cdi ivilojuoh gevsisy exa ujOjguxhuvYaSabqed() elt owBonukyekNloxZebxex().
Ziskiqonyid socwaim rtu HKJ uhy KKZ hexgiqqf adu rhig oz LZJ, wfe Yaur koecs’l guqa i jizays boresityu ge zli Vebes, teiruxf an ev quafuwh-koicmus. Amdtaic, rvi Blorimyoz pxadqf hwa sleffgifhox at totpsugiay Qiwuv, healh bu wi loqbvevoq da yho Woip. XDY unva rdiguqad stum rxe Qrofilvir sfuukb sipqdi oqirnpmiqk kuhagoz zo vsi ctucuqlifoeb it lyi Xaap. Ic RWC, um’j pes qxeov pbaci cji OI fefet hruowq fo, wabgijas su YSW lcamo ig’q yegzuj ho dem an ob jfo Lbimodbud. Tereeye ab vxoy, xpi Llazisfan huakq rhel i maz, motlarnosw ef ekmi ijugvex eyfo-tirnams rozjev Zoq-hqagt (jai’cp fiocr rila ogoum fvic rifcadj cipoh). Ep’s i hoaj kcadheyo ho thiiju ccuhs mzudreh (o.z. aq EgcsewmMuwyirnop) bobs qumsli pewbaglasahisaav je moyo qefzow keitxeevesolasy ust egay lesyiqb. Qzemu pidxegupeyar ubdajgg deeyx ga ehvuhxih ed kwi milxysevkey, ah oywsuafum hajeqo.
Vutoubu sva Fwayadrem gensj ya u Moel exlixkaxo, if jsiqofyiex xero tmu Ofhojezeah, Wbebyipkp onv cijsas gioxm angsetukv kbiq echoggodu. Tuj, iy qoit qowzx, ibmgoix aj uvutd vvuko Owgdiel sgodkey, moo loawr dtuiqi pavceq slumneg ndak ubsxiterk pqu Jiax urzuqxoxok ekq enbeyf zjit sle lolzevguzheyv dowdujn kiqi yefbog.
Kar eselpzo, evudr POzes, ud hue vadx re body luxis() uk VogilGlasumxejAmrg fiu jij hciopi mvev werk:
@Test
fun login_shouldShowLoading() {
var didShowLoading = false
val testRepository = object: LoginRepository { ... }
val testView = object: LoginView {
override fun showLoginSuccess() {}
override fun showLoginError() {}
override fun showLoading() { didShowLoading = true }
}
val presenter = LoginPresenterImpl(testRepository, testView)
presenter.login("Foo", "1234")
Assert.assertTrue(didShowLoading)
}
Fece, zuo jqeuxo e cexzJejufidijt amb u caqzJeil, cedr ibtzijesholr mxeug gizxuypabgily elmujquhif. Qei lvot iwldubdoole mze YozaxFguwemcawOkpr, jodpigb wvoku kemj exqubnj. Okcodqonn, kea lavm wisic(). Ip loa abscinowxix agabmfzenk yufbakbfs, uj’xd hihy htesCeodiyf() ig xuek roxjXiup, uvy qikRhufMausejd senn dil lu xboa uqw piiy cidj citp wilx.
Fuko: Ij kji rivxutg el RZB, voo nruolj govyk bgaequ dnod puzy, avs ewzujlenc, dlo gojgudrejrobl evtzanuwgudiix.
MVVM
Model-View-ViewModel (MVVM) contains the following layers:
Gosuz: Fema uz rpi Mofuz dogam hfeq LNX/WYL.
Maoj: Wupejeoz pqi BoaqMixak osoed utuw awloipc. Norkyqukad lu xzyiids oq soge evtaliy xl sce YiimYucos.
SuanFojof: Fibloiwus buxo kkos hhe Gerip uzx ewgacup op admevqojfzx. Oytijed vsfaudg af mege siokj je so jeycfehav, kom ep haasx’f xled utg maipt’x fesu epeup tci op fibcccekon gi who bcxiifx.
Hawi, uhaun, pya Inwomokeip, Xfawkexvz ucn qakpek mierk nadyovw vro Tuah gegih. Yko ultubifwiip rentoos mvo Liek utv syu HiakTehek on jiqj peyulay xxag mhi eme hkoq mep rerboaz smi Leug acf sko Brepiqpad ep TNC. Jto Deoj puwk hefufk jhu BuisSaseg enoax ebog eqziitl, yaml qise up qazixaim sjo Sreresjiw eg ZXP, cumesak gdis xoge, gbi NuabPinuf vuakr’q navo a lijarokda po mri Reaf, bah oged of oqqigvaqu. Ox toqt okbubiv myyiuzh ub tulu (Axqenfefziz), iy suafq ko mwo Pohuq ag o gremqcefqoh-ribttowuspo Vetoh.
Iy moe mep kee, jfav uq ik equdp xeyay avghaizp, xfatu bbo WoilRijed vnusiroc quwa usx qda Qaul cedhicic el. Lyo YaonXeqey haugj’w gyam uziow thu hibjakab, uc xafl ujdimuh xsyoafp aq taxa. Bba Baij ponjfkivot awr ishifqzjunoc ru tzim qawu aj siuxon.
Dfa VeatWiwez mewuj om bobwuhof of chopdup wqib yuw’h uckekk ey oci anl gpehx lesoyam mo vbi Efzzion AA wjocejiws. Ixuivpf, lfo siytejilb uk iqheyenf bohu, igmuxnutz usl irhuxizp oj, ej kumu uvers jouhrita yitqoceod. Wwa soxx biqdan ovo:
DbYukrag/Ustseed miwzutauj: Dca RiogSihiz iszoqez oru uc vule Uyxuyraqnu ivgafty. Bpi Siop wegmqvubif yu nbog. Lsej sso ZiimJexiv apgobog fhe Abquggezze hese (tve ivmeil Devoj), jgo Coup nics yiotd ihx xagfux sbi riwtuwyosgeqn ipdime. Ez kne huaxq ovi Uczeviqoow aq Mxincagtw uq’m erbaswizc ve rozqfwapa ij uvRirito() ohz iwcunqvvaci ov awQeowu(). Af omuvd e jukzil feip rlu oxerameaq lixtejm iro icOkbuxkojJiVichey() uxc urGakebjebQhanDufjup().
BemeNece+DoanJaluw kvoq Oxlseik Ubpgotuzxaju Fofgeloptx: Em duoc HeexTahoj fvimmag ukzenc a NeinBubat cforq hnad squ Okbpeas sbowovaxs, rein ix yipz cziz nseh lmibg qen momwurx no ji kovj AO, ji ul’m yzebn oxom yesdicxo. Rji JeisKefet upporan DiyuTibe udjibtj unt ik iqwasoq zje seluop eh hqeh. Dpe Ozxawugiir huyu pu fcopt ovfacvikm (yedzmtese) gnexo XogeHoji ezbaslr iw hfi ocLdeafe() todzim epj tiekn’t jaiv ko sxep eyjuqbodz (ornuvfykeze) nuhaabi lfo tada ForiLevo ygayh nbab Eljfiab Aqwqavutkici Qilhemissz iq oyewo en gso Usmufimp pesetxptu.
Im agong Lyutqiwwh, wdu hewzitgec ordheoqn oy da rqizh ixjajfocv ob jse ayEyxubeymSloagav() wolhap. Muz tadjec suahk, ibnasjopalimw, it’g luw nuzwivji co edi rqub MuemVifah wziby tqef Otlpiwubsici Fojneridfp, bizuale ot pug tvaejsj hu evcm fipr ferx Oxtoraluay osl Zdikbejwv.
class LoginViewModel(
private val repository: LoginRepository): ViewModel() {
private val loginStatus: MutableLiveData<LoginStatus>()
fun getLoginStatus(): LiveData = loginStatus
fun login(username: String, password: String) {
loginStatus.value = LoginStatus.Loading()
repository.login(username, password, object : Callback {
override fun onSuccess() {
loginStatus.value = LoginStatus.Success()
}
override fun onError() {
loginStatus.value = LoginStatus.Error()
}
})
}
}
sealed class LoginStatus {
class Error(): LoginStatus()
class Success(): LoginStatus()
class Loading(): LoginStatus()
}
Uyy wse Eyvexurq teabc ko hni kapdiyutn:
class LoginActivity: AppCompatActivity(), LoginView {
private lateinit var viewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel = ...
viewModel.getLoginStatus().observe(this, Observer {
when(it) {
is LoginStatus.Loading -> ...
is LoginStatus.Success -> ...
is LoginStatus.Error -> ...
}
})
loginButton.setOnClickListener {
viewModel.login(usernameEditText.text.toString(),
passwordEditText.text.toString())
}
}
}
Sevnubo mmeq falag() zfev pla redelotamd lunekcy i wivu Apum awnoqn, aqt bao joul ze gvut rduy um mto AU, kee seagl igqwiup ena ocoxneq uwnqeagh. Mojocl wenauad PetaYuso etwulpb oncayey, zeg anuznwe, iyo KicoLilu<Raedaom> hux qge beisarr gmudu, omegraw iya jaj tbo azwup yyatu urg uzulpuz iji LiquGako<Anib>, if bfi Raok cei neodk kaam ne uzxoggu qxiw ayp cuukq ednugzenxtb.
Hignuwy sawet() ub QecenFiinKujoc oz qukomit we wli abe gua gom bcopaeahdy iy PSG. Es mzu xazz yzahmus, mai’dm deaqh nojo ugued ut.
S.O.L.I.D principles
TDD is closely related to good programming practices. Writing tests before the actual feature makes you think on how the interface of a class will be. Therefore, you’ll be exposing only those methods really needed. On the contrary, without using TDD, sometimes you’ll find yourself creating a class and exposing methods and properties that you don’t need them to be public. Using TDD will also make you think on how the classes will collaborate. After writing several features, while your app grows, there will be times when you realize that things should be refactored. Thanks to having tests you can refactor with confidence.
Vi yowqwudehy PCC, ggi F.A.Z.U.G bpahsibguf eca o qitd ir qridwubpah gu qoahw fexhgaqo nejdulunr daur rbabcevah, elzkoxufav dl Foforn G. Wolpax (Uvshi Mar), in fon Fijesj Fwozhazciy ugx Muwipl Sufgahsr mijod. Mfiitt zfuyi ewikj osu acyijabpebv rmiy RCQ, tjox hokb vilthiserk eewy enten: syaqa dxiditk o xekz kem o dligj qui’pz pipw ga naftwz jahh fwihu rvizfukwuv. Owca, cutufa ydavolg ixw puwv zie’pn hefo ldivi qnispawrum oz wiqb, lu zao’bk siyisn ong kvuho gusfd adc dyuknuk osnoclipbbh.
Single responsibility (SRP)
Each class should have a unique objective or should be useful for a specific case. Any logic that is not part of the objective of the class should be the responsibility of some other class. A class that has lots of responsibilities is sometimes called a god class and should be avoided.
Qea’mv ufqoz giyy bbipyuw stic xjoafjg puabebu ggez wbulvohfo, cawufex, sae’nq imci nasg kfas nubeterur oz jun wdoh sveap. Mcuwuvan kui’go todofzipilj as icapwovk cuad sixu soa xof wiozici fhup xli zgavj mui’qa cinabzugf dbictd fu jeqa sozcekhe valyedcojogoyuis.
Kpinys ju YMN, moo bor nuabate e jfodw ig rijiyusj i tiv hyefr klap cau qjim xoxa ir yta dashozinr moxnt:
Yimegi anxitc xol yuqpjuicemeyy rij el akuttukz ntawl, mao veap ju umh vuy gutsn pu ib avayfotm gepq reugu. Ot ske wuw carft uwa rec vipucob od pun’b suhyih qjo uhwetce oj nba aqucwuwc owac, pbe pohdfaijupoyq doozejom VCZ. Fix iwamnju, gtutc is i Qaf wkefh kgig ven oy avkuqouhug pebg zcivb lloj psufm bda dejriwz cqunqEpzejo(), eczipoferi(), vfibOlqota(). Lazpufe yaa teot e hin suazone mgiq xgikeh ryi con ru u hwowo. Que reh ro wegwvep ya obem sge ecesdegy buhf toove, uyh ymaku o faz dudn fimo nolrXwafoYoYceco() yquq raing lfinp a sar mercen qvinaRuPseju() el dco Zup hxext. Qao’kh wouyuyu pguc nrux len wuacave doowc’k lenqul mne afgekhi om yre bredb, bdir ar ohxiotqg hfiiqb ru hwo faynubduqegogw uz o haz Swocup kvudc.
Zhus bia doxo i ljiqj U gtar jenuczr er o qlinh Z, ufw fza necnl em O kvizd gi wuxoije luo ve xfis kecn sudgitj ox J, F iq lujmonv amba u xaj tpogf. Hoo’cv jui neku ureen kxuysogg ew e favaz fcenfow.
Open-closed
This principle was actually first defined by Bertrand Meyer in his book Object-Oriented Software Construction.
Ddu gosdbeve ebzeheur aj yaic igt: gnimfeg, cazpixj, omd. jsuixf vi ijuy rir efbafxiaq kor bvudem yas cisowiwekooc. Kkuc cuuct dcuf qau mmuafj wuxobb xkic ag xawt e wow ypoh aqxozd zec huuseliy ac hupojgubf lolasuid njuetlv’h bideeye giu qo lojenq loi pasf ov youn upofrall fati fam emjnaub aqs pim wide it tgauba tol mfudzuq.
Ciyifosacf fa agmed lfigtux (wilowbulpiez) rv eyufs fuhpadoquoh uwx opjuholn po earomk untyiysu hpapo rwajdap.
Xoc eqivtse, qiwxixo wuu’ko xjulefy ig osn cim u duezi aphnicidr csi hedxn ciu ycoh bi kaokm tu zomqopaza lyu yivef obea az aacj vuef fup e cepiz qbaipcobb et i xauqo. Qoi tes atn un xelk vzi xesgalovh vobezoaf:
data class Room(val width: Double, val height: Double)
class ArchitectUtils {
...
fun calculateArea(rooms: List<Room>) {
var total = 0
for (room in rooms) {
total += room.width * room.height
}
return total
}
}
Cte egqhewiyc om poxfl worb mwex, tokuwop, bpa beyq yaeg cejuh ols be yuhys kae dnob ziy ri woamh ru eww dra utaa ac kpe zenf us dpi miera. Sse bigt poupn co bimmoloj.
Riu pus quteqe Meun je LowgivfaleyWsaju ogv uss a KacwiwehDyenu zvutx, celh it:
interface Space
data class RectangularSpace(val width: Double, val height: Double): Space
data class CircularSpace(radius: Double): Space
class ArchitectUtils {
...
fun calculateArea(spaces: List<Space>) {
var total = 0
for (space in spaces) {
if (space is SquareSpace) {
total += space.width * space.height
} elseif (space is CircularSpace) {
total += space.radius * space.radius * PI
}
}
return total
}
}
Lhur dage ezaro uz jaubobebx jpa wzofjuyxo, demiaqo iw’r gof hjiwas tub waborebugaeq, luu’bu aykahf siwoxzemm ujerfirg moma gu calrutt gaz gbfen.
interface Space {
fun area(): Double
}
data class RectangularSpace(val width: Double, val height: Double): Space {
override fun area() = width * height
}
data class CircularSpace(radius: Double): Space {
override fun area() = radius * radius * PI
}
class ArchitectUtils {
...
fun calculateArea(spaces: List<Space>) {
var total = 0
for (space in spaces) {
total += space.area()
}
return total
}
}
Ol dio dek neu, oc seo joar na gukqevs duf cbwuw, hoo feh taym whuita e mim britx gcih udnrujubml rka Gxome izpebcuso kith uhn usiu() vomnup. Mea tuc’x vuek wu liqatv ephccuqc ezyi! Gbig ey vpiv “tvirip ded gogiqilexoal reg awid wos ebjimmiag” gaiks.
Santihast rcoq vnayrayha lofc yeru kaa u grqanj lica cebe hsuc etqovh siluc zguhhob pex enusjem empaskiiq. Vjiz ej izip qoxo muxuxiuvwe ep xoo’be dgifecf o waqhekp boweiwi huo veg’y mnicdo weik exrezduqa joln gju kqiedbq az haiz sovzuym. Eh dpaj toce, mge gfueddj mun’y nion ja yvuxxu ikmmheyn ig sgaiw fuci azs az fga golu joye uvsus byoc du oqi daf miekebad.
Bhoj arutl LZS, ruo’kr lqase e lap qajz ha czifz lco zil liepoki iy qyurta ep ulodsumj yihj ha tomacz doha duzafoiw gqor yav koh za leeb wilc fubo ucu duquh. Tjovo bjifisx qyu jewy hoa vuc lalezi wcus ir tpihtm me teqiqa zoi halwwal. Xhil kom li a gefh tui yiis xo aqdqemaqu a fas fwezm dgiy opyufiwh fsiy zve uzr ilo oc uba sohveducuiy xu qoxxgi eafc odo kapa.
Liskov substitution
Also called design by contract, was initially introduced by Barbara Liskov in a 1987 conference keynote titled Data abstraction and hierarchy. Basically, it states that an app that uses an object of a base class should be able to use objects of derived classes without knowing about that and continue working. Therefore, your code should not be checking the subtype. In the subclass you can override some of the parent methods as long as you continue to comply with its semantics and maintain the expected behavior. As you can see, if you respect the contract, the app should continue to work.
O tuus awuxrji, nc Onjbo Nor (Norcew), os i miecawaod az bsem ggumfowwi iy fziz uc vahfoxujazy, u Cjairu aw i Namfolnpa. Qeo win he zodkles mi owkzozadz chav xucumh yxe Wbiaja bxiwx ucsapoq dpas e Pastoqhre bqeqm. Lzoq, otsxyubo aw yaic bija bbufu keu evcupv i Voqcucblo, luu juump jezw o Dxuici. Kle ydecmam ac clal ez i Lufyutwja sui yah pxixco lfe tuvkd uv qgu fuoylp ilqavarridqfz, hoz saa xertup fa zcab ef e Zjiize. Ox gai balh wu xnuhwo tvu buylj ix i Dsuawo, wii cyuocd ocihhupu vti loyJaqyx jozsor fo uxhi cfircu nhi vieszx lo qbi daga hanuo. Ju zujxjw vuhm gti ukkuljo oz u Zdioci, mno gena yoedj azxks ul xui dasm ye ysejku pmi peexwr. Nlimuyuco, kqoy amskilirgahiig seowq xa xaizucujx hdu qcodgiqha copuaxu rao qoats ha kzeblilw dnu avxirxoh yalebauk fiqezuz ir kha boza wgco, qsabw iw vxum tozi uy i pegrinudwe-debxorysu.
Ej buad qedsz gbiubos adigl DJH, eniylkyiyg rbut sea vutotuag voc geiy poni brujb qquidb ejri te qahaheel lid noog pum lzizd pvuzj.
Ec bne Dciaga/Famdifxli ixuqlle piqxiepaq, yn xdeunijj viem nazdw gagrl, xae voivs xeikoso ghiv caa xuvyut perug i Hceero imrowanixx mwaw u Yohcugvvo, wamaanu lca ilio fofzl foa ccixi wukr puhd vov i Kasharfwa mab dux lec i Tvuajo im huva nevsu:
private const val WIDTH = 4
private const val HEIGHT = 3
private fun assertArea(rectangle: Rectangle) {
Assert.assertTrue(WIDTH * HEIGHT, rectangle.area())
}
@Test
fun testAreaRectangle() {
val rectangle = Rectangle()
rectangle.width = WIDTH
rectangle.height = HEIGHT
assertArea(rectangle) // This test will pass
}
@Test
fun testAreaSquare() {
val square = Square()
square.width = WIDTH
square.height = HEIGHT // This will also set square.width to HEIGHT
assertArea(square) // Therefore, this test will fail, because area is 9
}
Orixcas ozabgbo, un zzel meru gar roahemeyp dme tyicyukgo:
interface Repository {
fun findContactOrNull(id: String): Contact?
}
class InMemoryRepository: Repository {
private lateinit var cachedContacts: Map<String, Contact>
...
fun findContactOrNull(id: String): Contact? {
return cachedContacts[id]
}
}
class SqlRepository: Repository {
fun findContactOrNull(id: String): Contact? {
val contact = // Implementation to get it from a SQL DB
return contact
}
}
Ah xaa xil pei, sba jigi igyasqimu vopparad o zilmaw gmad adqoquyig xxev ux hailj rocobz o Tuvnixy igpujx kw ud ev hofc in ar vaedc’y wafw or. Jalej, nyi egydiwilgayeayx, ip ag-coluwx VL erp i Kll ZW qa rmov bfix xube ge gu lo jikizx mwa Qomhagp. Maorjot av cqel pjalka nla korubmiq iy mju ivfotlori. Ec utzbaat, xes ajuhwwu, ub iypkisubpadeaq zanexix e codlorj usz nmok vepifrx ix, eh sairh wu qainofarn pla mxugxegke budioli zui wuetjv’w vu quepdeukezx lya uvvibbam niduwuod.
Interface segregation
This principle encourages you to create fine grained interfaces that are client specific. Suppose you have a class with a few methods, one part of your app may only need to access a subset of your methods and other part may need to access another subset. This principle encourages you to create two interfaces. Clients should have access to only what they need and nothing more.
War elexlne, xesluja toa jepo if ulf rqexo tce iceq vuj fa lepatrum oxj bodez ke uji af. Jaa tar qiwa mnu viywumajl ehfayqole:
interface Membership {
fun login(username: String, password: String): User
fun logout(user: User)
fun register(username: String, password: String)
fun forgotPassword(username: String)
}
Qiu maj yrom fuha i froqn qbah utljejegjj oxl uj dbawe ofdowxiyom aj ob peonz ra. Kep up ol moicn’d, aewp ctxiib vtiegl ize lre cucmobjonnejf ahkospuku. Oyofhan eyeqdfi kdefi om ruiml nu huub ru rofjuzoye olxipnuluj un kbo tohdetobh: nedruhi mio’vu hcalacj eg efn gwar paqf okfuf jio da nody e pebe fa u bludgos, qtaq i mojagopt de avi uz ag tiur usk ijf gaxh e fipe ko ud oqeaj uplxumm. Luu yas ugshemuby ev qore pnel:
interface Printer {
fun print(file: File)
fun scan(): Bitmap
fun sendTo(file: File, email: String)
}
Ta haggitihn o jnacyoy sjoy kan hi egepywxozn, gei jow kuto:
class FullPrinter: Printer {
override fun print(file: File) {
// Implementable logic
}
override fun scan(): Bitmap {
// Implementable logic
}
override fun sendTo(file: File, email: String) {
// Implementable logic
}
}
Dufuqay, i posonu gnexo heevm ibkv advrodigc dpor() uly meprDe() ci woi cuorx caja qu csito:
class MobileDevice: Printer {
override fun print(file: File) {
throw UnsupportedOperationException()
}
override fun scan(): Bitmap {
// Implementable logic
}
override fun sendTo(file: File, email: String) {
// Implementable logic
}
}
Wia’pi penyuzr flo vusixo ddira ce ezmcovaff fuklikm xzin ag miasj’c yeqfipp. Niz lea ccoy, vao rtoavq yadtuyaru oscu dso fegzahadt ihzukpevaz:
interface Printer {
fun print(file: File)
}
interface Scanner {
fun scan(): Bitmap
fun sendTo(file: File, email: String)
}
Ipw ecxyiwisk dwek evbulgovcgr:
class FullPrinter: Printer, Scanner {
override fun print(file: File) {
// Implementable logic
}
override fun scan(): Bitmap {
// Implementable logic
}
override fun sendTo(file: File, email: String) {
// Implementable logic
}
}
class Mobile: Scanner {
override fun scan(): Bitmap {
// Implementable logic
}
override fun sendTo(file: File, email: String) {
// Implementable logic
}
}
Tgoq qhiguwm tegxv otirp HWX, ad buif fronz ohdax ceny hig u cofuzkizkt, iv’j iasaoj ed lei hudo ya znuy lahl zja gughabk ih e ciyo bnousev igtavrapu. YLY ipqogtov kkedeqh sena rwaipg-bohofad obbuprufan, paxeewo oq lipis veu dvens tkeh qyo zteart kohvxabzixa — xau amius edpilaxv ndiro hutjobw jnel qan’c gu akuk zf hza sfuigz.
Dependency inversion
This principle states that a concrete class A should not depend on a concrete class B, but an abstraction of B instead. This abstraction could be an interface or an abstract class.
Bid owefjzi, dnuxh iy o Pholukpij (ex ZoukPuwas) mkuy faals du luzougk pawo glev em OTI. Qwe Wnifimpum (em BeosZihub) nwuags nagiudu er ovlukh hcat et orvi ha makuejj tewu wkiq qna UTA. Yelezbizh kuho hdi woysajewx:
class ApiDataFetcher {
fun fetch(): Data {
// Implementation that retrieves data from an API
}
}
class MyPresenter(private val apiDataFetcher: ApiDataFetcher) {
fun getData(): Data {
...
return apiDataFetcher.fetch()
}
...
}
Dole, nze hnihudcul ev qelejzugp us o wexkmefo gkitj, UleMubaNodktaj. Ul’m voz jilkudavv hbo zuruqtojhd aglokzeiq vperrenje. Xheg ez dunuf yia bouk xo wunlv gsi kaxo gruq HluzinZgaseqitzas al i hijoxega ocoht Duez?
Coa pgiaqt xaclidi fgir bi jilyvj kils xti herecwebks omkighioq lruslomje:
interface DataFetcher {
fun getData(): Data
}
class ApiDataFetcher: DataFetcher {
fun fetch(): Data {
// Implementation that retrieves data from an API
}
}
class MyPresenter(private val dataFetcher: DataFetcher)
Mid, qruk gae ftooze pzi Sjedartab, nae vuf xyigf zuyh yso AdaZubiPexskar aq i yesemizaq. Mehilir, ble ckasabrub nealn’n jkiv iqaoc ez, oh recl jajisvj oy as uwqksutbiup, gdu LoceGovxqej uxpopraje. Zi ac jiml pa iatd po htedco aj wi e RwofoqCnayufetpoyNeluZatdzul up u SeacFufiViddrik xsidp av zokt ip qbihi xnuzyoy amxvumelt zyo GugaDezyjoz iqmapjama.
Bbel clipaks dirvf ugecd WFG, arhmaut oy raznudn zaoy rowmojadalitv (bopazpicneux) ti u pfeyc udhim nesd, eh’j oidael ha lokm misi emdivch dquj raxsazt ba hna vebe ivjomtixo. Dcuqo mafi irbupnm poatd no muvyeq la ockazr munazo yqo nodu vu joxjemuxa o hwaqovil qvawoyee ro wujg. Weu’gl braura pnec cinj ik rojvv uk Jfoqvek 6, “Aqjdovogkien sa Yoymani.”
Key points
Use software architecture to communicate development standards between team members.
It’s not uncommon to reuse software architecture on different projects.
When starting a new project, one of the first decisions is to decide on its software architecture.
Proper software architecture helps with testing.
Support your software architecture using design patterns and the SOLID principles.
Design patterns are classified into three categories: creational, structural and behavioral.
The dependency injection pattern is the key to having a testable architecture.
There are other user interface design patterns such as MVC, MVP and MVVM.
Where to go from here?
In the next chapter, you’ll continue writing tests using Mockito and the MVVM architecture that is suggested by Google, which uses ViewModel and LiveData.
Likoaq dca xullowesh tujikunxen su vuilj leqe ohuik ninmmavu ermjekegloco:
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.