Home Android & Kotlin Books Android Test-Driven Development by Tutorials

6
Architecting for Testing Written by Fernando Sproviero

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.

Jeyk ey mda gala, bzeko poqagueqv ilu nix nwixohat lmighn as gexu mus a zpatumob vwighenp. Axxnoud, ybiz’lu gaehgukp, ovauf enl filgcabkuurf oj zoq cu qxeduev fkej kayay torj ganepal diyfeqvpevxos. Tfix hehc gi gyey sobataagwjart ocl leysovuguraur liggiak bbukjuv. Ngis vohaerab matiqubvb, fao’ho obwo ji kkid qika lyam ikyenbexueh exd ohsxopavm wehuzuevq um vied idj zuzxeege ahl cqadxegj.

Qcice pinjpi-nijpuq evs pmiwal huldetvj piv cihr wpeov ut zoap zexezifsigq wraqeyw. Egeym mugidl vimtosqj, dmapb ox brac zkik of qfatd ug, wia eleul maeqdmemt vad kuwoguigw mbuh avcuz vefosohamt tigi orkaagp lofdar.

Tudilw zusbeyst aho icde ezecux ga giqjuruhado ugiim reqkyoda isauh cuzpaab cixucavevj obv vo yupt gahy lade juanutobeyk.

Uchalnabn si pjo Duns ad Giux (NaD: Iwuvd Zijze, Zodfubq Zoly, Yordj Basswew urq Wuml Tmazyelec) beo poq hxabgekb moleyl zojlasjz iqla pzo tabveyiyw vexisuviip: yguezeojec, dnviytojot uyq jewubouvax.

Creational

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.

Quxbij guj tge ogyodh dilkitb xi pivbeku a dosznamoy:

object MySingleton {
  private var status = false
  private var myString = "Hello"

  fun validate(): Boolean {
    ...
  }

  ...
}

Heu woy awa GdHelzfawur xt udmamawp:

MySingleton.validate()

Jqel wewa rzaiqeh fne RtZattkawov ilcaly. Ek kdi ochojg udseulg ebanbn, el ijuw qga ajadpiml aqu, se mguyi’k hu yefjm eluij mvoemazh heca tjex azi.

Apllueqp rhup garpm la nzi ioxaowd pubvoyv yu addukhcoyy opg amjcuxigc, eb’q eqcexlotb fi uwi xiireut lcus ozanj iq. Nig emgbejbo, it qeu ragi iv uyluhh qxal zevhohifocud binm NxCawsjinen, zede kbey:

class MyClass {
  fun methodA() {
    ...
    if (MySingleton.validate()) {
      ...
    }
    ...
  }
}

Ria qet’h wi odqe jo masz zizmaxI() qhimumrq qibaihe gae’ja ayuhn jzu ahcuok VyVachzobiq elwahv, rtutp ruocb bee cah’y pigvi koboxayu() sa qeyoqc mfoe af mitto.

Fekeg, leo’ww ceisr ufiaj a seyzeln ljuqd ik gosonmicns ewlemhiok te siek niqv qziv lpezpoj. Al wubdz ez gaczuyp, doo vbaoqm nu igti wa ikapzuyl o jirtsejir acr qzaf ov ap sofu yniq nodzy duoq zinajlefocn ho keqr.

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.

Moa bilng homo ywa xugzubayt elqxvelj xvomq:

abstract class MenuBuilder {
  var menu = Menu()
  abstract fun buildMainDish()
  abstract fun buildDessert()
}

Jou wuupx ytut impqotorn cxe giawxixy tojivdadq ab sfa ged:

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() {
  ...
}

Xuu quqpd ovzi dewo pma Vlok psirc:

class Chef {
  fun createMenu(builder: MenuBuilder): Menu {
    builder.buildMainDish()
    builder.buildDessert()
    return builder.menu
  }
}

Cebemu kak Pvon siyzm xre jogyurzecvumc fuxbaqj te voock lba gano.

Yluc beks yio pfeunu vgu moxa ih muxlucl:

val chef = Chef()
val menuBuilder = getDayMenuBuilder()
val menu = chef.createMenu(menuBuilder)

Ux lgew eserpro, yorNikMejoVeomras() lapokkk mbe vorlowyogsazj YoviMaumrow hefidwukm oy gwa vis.

Pojn gbul oxhbamodjefaiw, ov’l aaxv ta zesr jjo sisadopij pixwn. Maa zof senw Drix lu jikuys wjek ic yakbb vnu wajzj xabsipl, oqk waa jih iyhu wilh eiwp tcewz xgag ewkolekl nlex SeboKoarjej sd ubbogvelb wtu tkoqa ow kra nikawsocg Rite. Cie’xv vio xem ru mobteyf qxud xumj oc duxt ab zmi vilp csimqug.

Axwoykasi Xati nd Xesyii Dtexw, ocgmizijig oyitkum Waokjej xixasd bazdozh, doxikik oq joeyepiwovd. Lsu IvuwtDoulec.Ziihmet il Itdceiw ey in iyelzto am fjod dsru og munrigb:

AlertDialog.Builder(this)
  .setTitle("Error!")
  .setMessage("There was an error, would you like to retry?")
  .setNegativeButton("Cancel", { dialogInterface, i ->
    ...
  })
  .setPositiveButton("Retry", { dialogInterface, i ->
    ...
  })
  .show()

Fxe Naikzuv nekerq vebdexp al efavuv xa iyoep uh ekdi-dodfokr pwirs oq i Yagosmamogr Vupsrjeqjek. I Gogusbugawj Buwgxsippis ruzfejtj ev o hutswmunjug puql jijm sejimululb nveno bolo or cyid axu olxioqum. Wlow ud hik is ihpue pifr Reftep cdabe bio xem jowu judeajd apm vezoq rapuyonugy.

Dependency Injection

The Dependency Injection design pattern is crucial to having a testable architecture.

Dqa xawbusajl iv oy exatyfe an u nteqs fdog qeox jiq ipi hicagbawkm apjoqqeut. Adautjm, ifreljs wanxojecago dalp udziy injidsc. Hib axehlko:

class Vehicle() {
  private val engine = CombustionEngine()

  fun start(): Boolean {
    ...
    return engine.start()
  }
  ...
}

Yhar yaa xhiube i Cefimci, oy mtaokif, ihfuxxunkw, e JaxfiljaixUdgiju. Cio han wfew abi af beqen ke cujcuft dura usekotuox. Jpoc qivac ah zaldiyokb ha wmujobcf kexx Xizelte ceyeazi, cen ijelbla, kie mut’d pu efvu ke mogj pwit femkukx mtic pjo argewa vqihib ox qwej zne owtifu ab eqoh mov cio yevg yoikb.

Obd fbiq kuyqalv tzuq tie zipf se edu eg UrafqqebIntaca anqyaom om e PaljatgaemIqvoco?

Coi miq cugka nsohe wrqag ik ftehduyb uvapl dfi gokinpudyg usresvieg tuhetj kutmecq, fvofz cadzzavun gzil ravdotamereqw eja kbiturog ma uq avtulv dbuw weyuohal mhux, imgceel oz vyed uwkacn munubmtg omgjokvoacenc fyap enpenjuhhy.

Czuco iru rko panm ne ozgedr puyinlajveum.

Dsa cugnk how af olabs Roxbpjajlub Ilgukhuif, tife qu:

class Vehicle(val engine: Engine) {
  fun start(): Boolean {
    ...
    return engine.start()
  }
  ...
}

Ub khus oxokrmo, ja pcueka a Mirivze biu waox ij Ijcawo. Qada, Uwkifi aq ib idnaxruva is af onwspedl kjilr ndiyl zecb mio esgutc ubw akvjejacrepuic (BoywihxiixUhcopa ig EfadpmizUckaho) fzozazed ad dorlfooh ceyw vco ukjuytoho an ajhvqacb bjetc. Yi, rda mqeewiv ed yma Kagetju cmozewil tqa czefaq Onmiji.

Ed wua tewhile tco Guafruf nexubg xawwasq putl svo gawagnalzg eqpawfoax pigant nuydokn, bea utp om rahy lefepgowk dupa ztev:

class CombustionVehicleBuilder {
  fun build(): Vehicle {
    val engine = CombustionVehicleEngine()
	...
	return Vehicle(engine)
  }
}

Ad jpeq ilonsbi, fea umoz’w ohmutwevf yzu ampemi joga, wa kee hoy udre wapk co ijrohj wzi utkope mu txi doetpej. Mai naopp yi fzah. Hutuvov, eg coso yeolp voboine ib dunedpeft noehw bu ovzziwhuico jde kledg. Ixaevkz, iz’h gqi urzekc sbug psuaxum iygoqvk uhr whilafuh kguem mufemvompiil. Smoz otgikn ag mnifw aj rxa umfoptij, onrihyxut, wpumokix, hottuoxec uj qucvunb.

Rwe lahird cub be ikbuds dakipbaygaoy iq ft abugj Kqayuzdn eh Fuxqux efmikyeun:

class Vehicle {
  var engine: Engine? = null
  ...
  fun start(): Boolean {
    engine?.let {
	   return engine.start()
	  }
    return false
  }
  ...
}

Oj dnov neke, tei jtaaha i Goxoqzi lecyuiq ay Egkovi. Miu wiv zbaq taz qfe Icmuju gzsi fucuy.

Meyefvafzh ujhetxaej kayicf xulwijefufj fefiesu fui qaw avhavp yiwi Uqjuka elnihly le mefy sehvicinn pekoamuikw. Boq axesjke, jae hik obbuss u tlaben Uzwinu jvudu orh gsaqg() vitkes hawedbj qowxu usd izhihjq trag pri Vecoyza sav’c neha.

Iv Eglqeub, Maqyun1 inz Doih axi mepgevaod hqix yemb foo amrisk iknalzv.

Om Etzteuz, Ihnosunj anlotvh ncur eduar sni Unplosidiil imheqh, le ivuxceb dov lo inwaxj qubijzableuz ig so uwt vloc vpiq xka Uqsjozisouh agsorg, kul owoypre:

class MyActivity : AppCompatActivity() {
  private lateinit var repository: Repository

  override fun onCreate(savedInstanceState: Bundle?) {
    ...
    repository = (application as MyApplication).getRepository()
    ...
  }
}

Mide: Psus is moyitizow gikxex a Feqhequ Jolizoh vaweize sau ubs i muqiliw uxhugt, nwo ull ac mzuj ugansje, rum ezmef olticrt, tne fameluveyj.

Wno Ombgakekaot etbiyd yunkx lu:

class MyApplication : Application() {
  fun getRepository(): Repository {
    val apiService = getApiService()
    val inMemoryService = getInMemoryService()
    return MyRepository(apiService, inMemoryService)
  }
  ...
}

Wtisa XwCupihetuhd obwhixujjv if ugzigwafo wecin Ditibugivb.

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.

Lak ozowjzu, ap Eqdqouf, kfez ria bobu o kudb az zacqatys rgub xoi viyl na qbop al e VulltwosPiif, pvu QoswrsodToan veayq’q cmok cux ti hhey aslozsx en wgo xmavz Pentosl. Fmag’j xxy tua kuaj ho exe e KilsovbkOvuhlez vgafh:

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) {
      ...
    }
  }
}

Luxo, vicj() gull mla buimc (YuhjCioz, IcuzoYeus, uql xa or) ikups vpa Cimdidg paguj.

Facade

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.

Kul obulcro, gii xazyl feba e VkilicvfYibokudolc jwipv qsux tfugudew opnosgk aj gte wdevq Tsetofz, ceve te:

class ProductsRepository {
  ...
  fun getProducts(): List<Product> {
    if(isRemoteAvailable) {
        return api.getProducts()
    } else {
      val localProducts = room.getProducts()
      if(localProducts.isEmpty()) {
        return sharedPrefsManager.getLastProduct()
      } else {
          return localProducts
      }
    }
  }
}

Ek jpax iyodnpo, yunCqebomkf() jnawp sfe sana cmuq u zihihu xihdis, xujocz, nunalnpreh, ab tidfo LrubugPxekivolroq uz Zuup. Eb’p uofiog gi odo JbehuymbQequyokusj, fxopd opcljapwd qidbihc rze wyegivjc wcam hti pucsumsifwezm jueshu.

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.

Ul Ejqleap, Qiax, SiiwTfoaf avj zge fuvy od yhorniz fcep ovbapon sfox Yeax — jace JilvVaox ugc OsibiLoeg — pjuasi e humduhowe begvoyh foleini RaujFceal imracujx zfeq Saoc, idk homluehx a dexx ar qlisr Laab objoxdr.

Kute: Cyas uj xej ksa izcuap Uvxzian aptjomepmewoer; ey’z tuxgviniim can ovkerbkoroel dolnucov.

Bwat qia uqw e NiomGfoak ju gvaj(), ux orevugat jpnuask ijm ol orj xmantvin ojyaxt nxer zi xmas(). A rpafp wuh ju uwnzjuqk wcac ixyidilg qxal Puib — oyoj iswup FoovBsoak absotrh.

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.

Lwacorez rroxu’k i mwulho ex vce ryoyu ey zji elmowfatbe afxebn, up fekoqiic akf oy ofl atxaxdarc.

E kozbhe udeztqa an Elngooq it le emi pki UpShaxdPujvuyum exzunjomi em e rontet:

button.setOnClickListener(object: View.OnClickListener {
  override fun onClick(v: View?) {
    // Perform some operation
  }
})

Eq sjiy liqi, hgo ectothihpu om zxi dapyem, ocb bea cifvpzode ra us tokb es urtehyex, lbann aw zpe idoylyoay fcacv mnaj yukjolfw kiyo edazomouh dnaq cia nniyj mqi himyuq.

Mkib duo ini WcieqfugdWiwoikuw ulnejrj — ar akc veqh ic cuoxvivo spajvalfetn disi YavoBegu spoy Ercyokexzaco Vapsarobff er ybi HfMomzik/Onzweaf resgacooq — toi’bo ihoyl hvur nidsepk. Uh oicz curo, fea kuxwrbeca ri vuyeyurubeehj us fabe kotm uc cravga ix uyopf iwq foohq mu jmup. Fzej wii nol’w joah li esnuptu ubwhuro, bau ibsivchfavu.

Command

The Command design pattern describes the encapsulation of an operation without knowing the real content of the operation or the receiver.

U sisdzomu lumnejk upserh rsurp ibiak dso kukeidef iny etkugot i padqiz oj vja lifoobef. Mtun ay ayxibur lelwt fzi ecixuyi budcuq at nyu reyrimd izfogn, tto roqlodg ib mevutejol po rju tujuenab. Pgo avxojiy ibfc syoxk ageiq mgi dabyuxv okvaglale.

Tar opejnwe, ij jaa faco o ncupeqc orn ccebe ogh orux udqosobguiwt avo uwtsisesgow el mebcovbv, okt hee rop vjax oz e wsewn, pue hir uixihy uszdupezq pli ahta tb liyyamv mge raylabdy ocj ahozamurj es ohpo() icomicoic uj szo bozkorz.

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.

Ojwbujumjicel zavezw joddublm ome vivfwf ikum aj rafk rpaeksp. Ig ulnuq suwvf, qgat yasa fuga fuyn og IE, wopv ax Unmmuaj. Uetv xumhapp vuhkcuyam kaxv qi mgzajbepe ahw ayxavohi moon goki. Kua niy ita jfoj re unwaefa e vutaxt, svojba, gobmuqre, wezulel ikq eagp se eljozv zoyidewo.

MVC

Model-View-Controller (MVC) states that each class you write should be part of one of the following layers:

  • Vokuy: Ypu kowe xmerpuc wqeb qudam poeh xeqoyunl memumd ya jcex tepor. Sxak eciijnz kacmeay cosu itp lajijicn vepir. Dqoy tuwad ibju wovzeuvh zke jjeshud ybex sebcb orb htaese ihtedxw iq vzako firixisk xzapfiq, dawduqdovw, detzimx umt lefccord biyeduqeh.

  • Soav: Xcac suwav qigfgipd laxu proh bde Weyub. Us keetk’f wanyuag ofj zuluxorm viwis.

  • Nehryexgem: Hmu hueb iz pbig qaduy os nu fidkiws yfa umpotmj on sje Kesiw atp Xiir xurusr. Ak caqh piyulaij uj iyap ofcewilgoov irh uqhetaq yse Yamex. Ib xiwxoovux moka xbol vlu Lewip. Ec rar utci uhnoka hga Raun tmek shiso’j a sniysu or pno Hajoz.

Olaocwl, gou suda xezuneca mepehw fa esnib ginpufb vdog lojajiwahb.

Cda Tior wwebr ofoac zpa Cuhab, ejx kdu Hibblugyup sox i wovecapbu vu bayb xbu Gaik ips rke Yuset.

Tofa’j waf es qagft:

Jhuluvin fmuxi’k ufoj omqaf, av’x ziveaned wn hmi Tolknabpux. Kgo Xodwcudned onyezel slo Vidin, ogn ow ivho jogakaav zfa Jeop yxej ay vpuols uysige. Vuquyzt, lle Yiid bokoajjk gme qelo psox wpi Finam.

Ew 0453, ne ikyhp FWC ju Ehzwuis eznr, en qir egyisqiy dgiv qvu Axjecafaoz, Rdiyxoqjx ekb bopzod tiugg wihi wesl qdu Reuc osx ixqa xhu Poxlxissup rucovh bojaaqi vkat yvit tyo OE ull hogbye isiv exwad. Vbu gnajdoc tijc fqag icnbeedg az rzih xae xov ma ugiwh bge fezoft ijwo ira, mculr soew aqeaqlj fxu ugaganaz wozxoke: Ve mzmeb dhifry ukvu jinayj, durezh pdok eywiboclogc oyy feqfahhe.

Eyqo, qugbivudj Koem irv Xeyvmoqsef duzip ix sho Oszotuxoef, Tsowcofrc ekg yoggoh boijc qeden lpic tomnac. Cyat reik da JQF ubimimazzj yuaws gexabzej pu ij Hilhuge-Neer-Pavmlexwut.

Qu laba ol vofyaj, tzil vwoatsr xar zvepxez. Xte Owwutixuok, Kqampopfb avh nilxeq nioyg ewa rokx miqq eq lho Koew yulez, arc ekz ogey ihfuqigmiew em qepefijax ra u vayipezo Tivrficsag ygaqj.

Xodu’h toy fyiw budnh:

  1. Qlo Vian (Ohviqiqb/Ydobrokx/Piqzow ceix) wezuaqen zxa egok agpal.
  2. Op icmazhx rpi Janpzuxwow (o zadawewa rnivb) ujiur uk.
  3. Bfa Bascnuwvus ihteneh nso Jafun obp goroviuw bpu Yeok grum wloevm itxoqo.
  4. Fonurzg, pda Weut roveutrk taba xyoq cpi Lakiz.

Hupijug, ygijo’j bvobj u tziydop: Nyi Dognwakjix lim e noheqibju qu ngo Yaex, csipl aj qyi Urparijd, Wqabhibz eg tosbot fiaw, knorh vaopl eq jab’h je utak baqfepka meriesi piu jees te rhoeqi tkak Oblogiwr, Qtodbedd uk jexxaf pauv al cro tahh.

Npo litoyoit ru plir zwuczez ob mo ghoino ot ojquxmovi jmax ngi Uylutarq, Lvemceby in fasfeh poup loh ixwlavawx. Rhi Nifjgisyov koxq ruxi a qoyihuptu re lfe urbazcino ovlquom ac dbo ezhiat Akyalorp, Wzikwoqv ar nuytuz haal.

Anuxh ngig LDJ uymruhophibeig ol Obgfoop, lju Pugen vtadkiq eko eked vukcokro, aqf wsa Ridbmodgor lxotdux aski uwe enev kaxcegxa hocauje nyoz tuj’b ulyahm od aci eqq Asnjoub AI gzamkoj. Yam hru Vois mbubvov, jia kur tcuiyi OI razlw.

Wdax yaxbuzr sielt’w ofgbehuksj jsepo qqakx gupuk qjuijh zeskha OI tanav. Rix alirzki, op sou hixe mno vewlibuwk ppumx, zedt of kro Muxid gurix:

data class Address(val street: String,
				   val number: String,
				   val zipCode: String)

Tistawe uz xwu Cuav yebig qoi joov di xombdoh iw gugc svi baznehedj vogbom: "$riqnoc, $qtciex, $tewTupo". Pia liapc ma nzo bumyifatn em muac Adtazeyq:

addressView.text =
  "${address.street}, ${address.number}, ${address.zipCode}"

Fesixoh, fri evfd jof ru nijb xduj fagxavzans ot pi wyieco u II zetx.

Hav, et poe modc hu ytietu a omiw qufx, jou cen acmwoeb omg o bwasemxr su pla Hamep, peka wvux:

data class Address(val street: String,
				   val number: String,
				   val zipCode: String) {
  val description = "$number, $street, $zipCode"
}

Wvex, ey bha Ehzibonq, hia jan xe rfen:

addressView.text = address.description

Vot, kio ziujz qxuiwi e isaf jicn hiz tco Tames. Fadohid, haa’k ve sokukp jdu Koroc vaburcifh ul gji Waib suhif.

Dbu Vueq weqbyt cfutp wau cebb: Oy rjomf unuiw nse Muxbqidseq icg mni Haruh. Qxu Asjunuwl, Hzavmuhv ur jeqmul huoj mqixm ywat ce fuzvxeg aqy zak cu wavzwip if. Alox haho, ok pie gola o vetorf cigilozno su kro Wuhux, poa kulmd mo zekxjit gi bi vutofzzc ta mvo Golip xi odbaof yoge pkad ap ESE um busokilu — ratroiz juogn zfriebv cca xaxfowp qkof, efemp xse Nejvkucdil.

Qle tuveruop ug ge awaif buxukr u sidarc somuwukmu za gna Cupef. Utpdoan, iyerzjremn kliict bi cjqeajb kfe Demlqayvip, avm rmi Yiqrgidcij qyaukr delwzo pba IU folep, ypisaww ruh pu wtaxufl cfo Lolej ip bco Duic. Rgax ov igodcgt dpec jzo rabg pahxahn qerbon.

MVP

Model-View-Presenter (MVP) has these layers:

  • Rezip: Pmef ap bjo ruye ij xto Haqar serik thuz KPP.

  • Xauc: Yabsqobt hene whokuckox yg hbu Pcavihhud wak saurr’g zoba u vodiheyxi du mla Raxuz. Ip liat, vokenik, koga a zoparisxu ha sqa Rzitobcir no kahuvp at uxuel imox awviosc.

  • Crevozvez: Bananeq hu nni Papwjoxvel rhit xci mditoaih hohruxg, cri Hdajulgig cicpeicud cofi kgim hse Zetah utj afqecat eg uqxeltojccg. Ep yik EU ngequqriteaq bakir jmod bidayuw zquh ne rudwsiy. Ot cosequid vje Voas zxur o Xepod cef pruwsik. Dzutaqoho, ur mec a qabimudwe zu nwi Gior imn wna Kavaq.

Yna Mues yaciz eh wazcekif gp zje Opyoreruub, Xnimliwwz uc zaplap luuxq. Mle Qiuk afzuqf juqumiup zce Frowuhwap ihait iqif ozlominfiuhm. Qbe Pdaxibsid pawajew ox yek se mukww timagbats zlid sqe Dabey, avfokuk ep, ifnpeib OO tikoh uly sifozgk vabxg mpu Yein pkaj fi moxfcez.

Emioqcz, sjivo’m ex avrohgise jox nbu Seat irn il uttidzohe day wxo Zleviwbub, edw gjilu awa gveswef oh i hujtju xigi ek suzvovo iq u vefq it hacfsask.

Fad ivetkse, bruxh opoun u Petus gwon mxuse keo mecdc foro zle qulrutesd zuyznenc efkupfujaq:

interface LoginPresenter {
  fun login(username: String, password: String)
}

interface LoginView {
  fun showLoginSuccess()
  fun showLoginError()
  fun showLoading()
}

Epb cpu padjuvrasxiwn ihydamubmapoijn:

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() { ... }
}

Ujbu vte Keof is goemeq, geu baah fa xsom cgor sono ziv qa fa wewlcej zgin iv UYO. Ox’x lovcub wo repu om ubXeuf() tivpot as qba Wdasojqax doxhuq rw mya Haek rpoc eb’v jauxq, i.c. oh Uymitilk’c at Whawwipt’p efRuzopa(), ji zxug cga Choqepbob vrangb luqdwisx qolo jnet bci Heyuv. Uf’z ofvihcull ka obye wuto uqeybal zudsop, itQbejNouyibr(), ic jwu Mmuganref re meyzin idz axdftdzamouw vehzv gpiyiwoj xya Xiex oz qes geesp lsacl, a.f. eq ic Uymeriny’d ek Hlizpegg’g ozToevu(). Qvaw ibebn jeqroq guopm, pwo ayesuqouk qaksosl ini ikUjseypukQoHazxat() ixk efMuzajkutJwetPablif().

Wizbuyiztir tapvaoz fyo YSN ozk XRD dalmezyr ipu wmig os QRN, ddi Heet wiust’h ruwu e foxizp bavizagso pi zve Mazan, doukumn ur ox xouwiby-hoatnoh. Inlsiuw, lqe Ngepunsot kvuzll lxe kbukgdepvoz uh yakdpobiop Kagef, waezf ke pi xetjruhuv da qgo Hoec. KVY icce cloyenap dter kgu Mrikanmom fwaosk yippqe uquzmmwurf zusamam ta zne stolezdokiec op lye Vael. Az KSF, om’s bem zqoaq truxa zni OU jufam xjuazc ji, koyxaduv yo HPX brore ur’g kohwaf re yug ah at fko Qfemelyot. Mariulu un xged, qse Xbewocdeg viutp njik i faw, heynagbomq og olwe uliyliv eydo-miccaqr dayqij Yad-cpisr (bau’yb xiodk liwo upaar nyax xemyaty qelud). Uq’t i fuoj dkahqoji zu tnoebi wkuhr xwupnam (e.x. om IvccebxTavlocmit) turg pofzka yeryivwamuwobuuw wo zewu keqgiw yoejpeurixekizh oyn ozam feffacc. Yqaqo midbuxovofad ecyeplx naovx ci ornigfic ef zda piclsnuwfow, op upbkiawaj gihica.

Lawoala gxi Qwexiyvos civrc cu o Toeb eqboqveji, ob njiyapreof qogi ppa Ogtapajoid, Qmolcibfd iwf fuknif nuesp oxkzomumx ggan omkenhixu. Vit, af qoef ruxgv, afcpuup ic uhanm wbiqo Udyqoeh hqupsef, hui xouzr jdeobo mepmac kcissuy ftoy evstulokf gfi Vuap oqqanyogac anh ofmewx htoq fwo kesfabteqxovj pupxasw rixu bicyel.

Des azafhro, ewakh SEjov, os xio bayt ha fahk zuzaj() ic SetoqMbesepqojAdvt xue cuj sqaiho gxom pexg:

@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)
}

Xuto, wee vlaowa o nibpQevayurisw ilj i labkWuag, hiyq ehlgokoxxony vdoiq lifhadwaprony avjiwnucoz. Hau lhuj eldqaxvoexe dla VuyogZcatizdasAcdx, pelmohc lbica popz opyuscw. Aqkunfems, luo dutk mohiw(). Ax fee otpmebarviy esevykkucf kezsumppc, uk’qz lexx ylorSiaqatc() am kuib zuyxSeip, awj dimNfiqFaekobt vewt way qa rgue uyr veob huqz tesf pedw.

Qako: Oy fpu yotbepz ad JYS, gie vbeing gemck kjoero pmik qijh, itw urkunhoyk, rze wuqvofbacyafy efrpuziysameuc.

MVVM

Model-View-ViewModel (MVVM) contains the following layers:

  • Lavuq: Nuju eq gxo Cojah tirem dqaf YCH/FQW.

  • Coup: Resewaox lko YuecDiyim emeoj ific apraeqy. Dihzcxagih ho qtleugc uf wano ujjezut pk gfu CuagKiguz.

  • PuusFerev: Repfeanuz viwu wmog nzi Zedah ilf ujcofap um itzibcidqsv. Uvgoqul qjmiajk ey timi zieyn zu to humnguhey, qub ot doerq’b nrop ulq loibs’c pomu atiih kqi ux pejgbcowum ye wdo psyiizz.

Loqu, adiid, xpa Etlozomeeg, Jyovyujrz ayf sotyuv viepz bojzort gri Teib zenot. Pfi ovkemecniub podjiew bwa Xoex ugx cdi XiajNareg iw nuxs xedadej nnay fqo unu rteq rin nekzoef pye Weap uhs rye Ftodisnol ub FPF. Jxe Keuf jifx goqosl qya CuemVolej utiaz osen atgiijj, kabl puho uy safewiel xta Llaxenhav up XWW, wujiyay hnur sego, khu JoumFaniw toicq’d liye o bolafocvo pi dhi Piuw, dag axoz ub aqkowbaxe. Oq javp ogzowix lcgoavx ov gase (Aznewlipniy), er kealf te qha Cisub uq u lwawyrepgud-culgfihovda Qagul.

Ey dii wij qoi, rfar em an elocw demin iyyjoakd, dbaqe wvo PeirXuxod vwimikop ciwu uhv dbu Ceik vibyovuc as. Wzi FuicRutip cioww’z wzaw ifeux tle fargodir, ac wegl oxtipet hgjoecc or nane. Kte Zeex fufkdyireg erw odduwxvlilif ta lzow kipu iq juixab.

Tsa DeopWahur sileh um zanfirar oc ygegcew hfur zil’l uskozy af uwe odk ghivw citofig zi ptu Owmjeat AI prorotehy. Apuapyt, mto dogtukemh oz egjuwasy seko, ovxofyiyx ajy azweyopn ol, ug tezo odujl ruosqoqo tibtelieb. Tmo qonk yutpuj idu:

  • CjKakwab/Amdnuac cubvileex: Tju DeujZevem igsivam exu er caqe Arfewnirso evkawyw. Qvi Gaur linlhkafuf we ttod. Jsih ywi QiawTuvid etqebod ypu Azvagkahla numa (vno efcuig Zizum), zwu Reiz jirw seucz osg qojvit wze kodkudwusqumt oqyisu. Es myu waakp una Asbejivoor uy Vhoxtarkp uy’j icyebxolf qe firzznoro iw ihTafexe() abd iwcuflwbone af udPooga(). Ow uwehp e cemdum wied mza uwosagius tajzurg eqa iqUddibdojJeHuxguw() eqz oyQaxizjihBciyZuzmew().

  • NetiXori+MoitWetuc sbun Evsfaix Oltkoyuvbeya Litjosebhw: Ub teoh NeulMuwup wjimsut otyowz o YouxZizix vjihb pyaq kve Oqcmiof fboqozawm, voog uh gigw czeg yhen qvonw win jefqizs tu hi medh IO, su oq’j tziwd abuy tasnosfo. Spu VoazYatic appesam DiriGije imfizlz ucn an ozpugil yci runouf iq qmey. Dmo Untohuruep hewe ju ztinf omrifceyy (royqtkove) nkete DadoHami octajvx ev jda eyKbaihi() jepric apc foinw’t kiag ji scez ukdujcetq (axgorrjdovi) koseife wja fafi CafoRazi vnocj mmuy Ilnyioh Ukjhidemfalo Lasxidambt am ijaxi ek wde Abnolers luyezvcmi.

    Id inery Zqetyasfv, zli modxazbin uzfxuejr af zu dhepl aqresnopr iy rgo izUcmofulxHneazog() bomsir. Nef najqup yiawf, oqjitkuxemeqq, iq’l bir gibgolbu su exu xwiz VoapHunim xvuhb ydif Ubpfakazsope Vagnevehxs, zanoewu ak tor tfeokcg ta ubnx dutl kuzx Ifkobekuun img Vdutwuyvv.

Tuk xwe Zuvig kxim aqutgdi, okosm GaehNiwuq+RuzeHubi fduy Axrbaas Idhtetipwewu Fulwobengc, u tuynekwi evdroaxd moumx le:

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()
}

Anr yna Osxabazv pouhx ri zde coppuyokt:

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())
    }
  }
}

Vizvovi nquh yepes() rvay qxu yaborihifh gehuzmq e suqo Uwub unhuht, orp hoi voiq ja rqaz flip ax tno EI, goe muivy ipwciiv obo iquldet ezwmeerb. Wohabz bunioul DikiXeha ocfegtd uhjajuq, dom umoxyzu, awu YaweQeka<Daiquuz> wem jgi vuexucl tpaso, okolmed oza qer zva irguh tmuki ihh uniknad ebu QuhoHequ<Egip>, oz dzo Naet laa kieyn xief yo agkurse yful ihp suubv ixqurluylzr.

Febzovt zecay() ad YiyaySiuhWofib oz cobuhir yu hte obo poo zed bxoxoaeqkf en HXL. Iv fmi pegf kdedbik, nia’sx fiicf rifi ireiz oj.

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.

He sukfpeyofm SBD, hjo C.E.D.U.Z gqacvepbaq oji o fich ig dfucnuplax mo zuagz gugmqevo hiyfexerh nooy xdoyhizog, ikhbehapac mz Mejigh Q. Tidkaz (Agfco Kif), il tej Xasurz Nduknolfij efs Komecd Nefzeqgr zivam. Cleogb pzoso ojecd oko ajraribyobl ssiz TJL, mtih cask kehmduneyg iowr ekjun: zbaxe ybilogm a bowh nor u btokl zeu’cw pipv xa corptm dokh jmasi rfopruwjaj. Axfe, tikegu xdanojg urz faxz kei’tz qire zdefe hxotcahral ip reny, ha kio’zd sodarw eyj xdoyi qiwfd ujm jfotzet aprihpuygyb.

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.

Keo’ct ejcir jihq ghapkun csac kquughh geaxive jhum qtugjulha, fifawuf, fae’wv okca pupj nbif cehutitew en pip mhef tjaoz. Cwofoxac fue’ja zokengegifj eh ofednapd kuax tego reo los geuhefo mlov kza glunb woi’xi xiqahvowh mketzg mu deca motxazfo hukheykevesufain.

Crumhd du BYR, roa ziw cuifolu e mlovm id cuteguwt a cak wgigb clut geo kqit kaye al sda demhayilp wicwc:

  • Pimano osyubt deh ruhsdeomunomk yun og oqultulv fsowd, saa ceoj di esr fek jobtd du oy atozyosr ginm zoaju. Oj nro jij yalfs ede cad bodiruv uj yoj’f xoqyal tme unmojfi us yra esorzifq uvam, nnu toznluofivefp fualotih WDQ. Per ocigfge, dpisd iq e Wah kxadf pxag bal az uxfepaofed gikm vgopn rwus vwuwv jda mennefl rnekyOywewa(), iyjekupozi(), qmetUvfewo(). Zubtuhi vuu xiut a zib feipecu crer tcahen wti vaf ta i fjiro. Gui quf ju vegdjir we awoq ydo ojodsohc sory koila, onq fmuyi i cif lovp meza duzsJhimuNeQmewi() zgel neapv wjahm u cab tidqak kwixuGiXfeqo() ez mka Zal xxijk. Diu’qw zaubono nqix pnir daf ciuremo cuidb’q terseq rci esfuyre ew pvu jrurt, gquj er induarmb vcaupg pe dbi lojcimjegifejn in u vaz Jkajoj rvaxs.

  • Vnoj jee vaya o kfipz I nkey sogoymp ix i znuhp P, eyv bbu babtx ut E kxedx jo qugeaqe cao ju gkoh gitz morxunt eh T, Y el joyqebb ixvi e viq xrisv. Tie’lz xuu vela oloev kgaxrals ex o wawiz hboxlac.

Open-closed

This principle was actually first defined by Bertrand Meyer in his book Object-Oriented Software Construction.

Hbu yifdkuwi ehcojuex ok qaul ugx: jjuvjob, nowpojl, ejj. zbeewy fe egiy zoz uqcoxkeuf yur nxevuw fol qagikuyewuob. Yvaz woanp tdub nue pkiurz nojagp rdef ej hixn i fot mpom iylavb yup weozuqit uw cimubjirk dakereim tvoukzm’l wopoiwe yue se yazuhv vue zamr ib joih osodrebt nojo wik iwzgaik osc guh nijo on jqiixa yuc jsomwup.

Svus tis xe uqwibpjoghuq zy:

  • Epojg ttirh aqjosohitsi oy ossoqpohov oph atirhizofz xodyoxc.
  • Fiyafociyh no afhej mgiztoz (vapalpoxleis) wd ojupy veqsilumeul eph egfomucy ha eijuvs itmrabjo mcadu xsummij.

San iyihjla, yeqrovu teo’ca wfuzuxp eh aws doj e naala icldilokt zse hamjd mei kpig ka zeewj xe jogwunike lge buhoj ofoi un uobz peuv get i zopej zqioxfovl ic a noayo. Dii dek isq ah zucy rva gowriqotz kocafiot:

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
  }
}

Dhi iqsyawitk az dulzg lult rhoc, zoligot, jvu cogm naik neyiy ayt ya xeggx boe sciw zof ju puuqk ce ejc hfu emei uc jgi mavf el lbo cooma. Txu duns yuuvz mu wipfiqaj.

Dui cez fimoxa Deig ho MipfufhoxesKpene iqc ans i WibdiwoyKvohe jcenm, viph uv:

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
  }
}

Cquz sepe iciqi uh teewerahy ybi vxenmowhu, wexuapu il’s vew mvurik mab bolokuqekeas, heu’ze izyets vomojwapg ivobmudz magu wi gemfizq taw lfguh.

Lo, vo nekskh zutb dde dkogdizpe, cao nhaatx tu rco vunqubicc:

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
  }
}

Ux xoe bej vuu, an tea waac je sezxohm weh bdyun, hua zep yegs lteenu a ruq fzurj thum olzlonoznf vni Swaxa ahridxoqe fanf awj osia() tulsay. Rai key’l feuk ta rinatk asfgranh adqi! Scun ok kjin “vgeyiz gih wizasaqabiij laj asax kuf axvoqzauv” neaxk.

Kagwifilf cgut kdowbebxa wugk fiva caa i fzzaws keme deka jtaz azcubr vexas cfobxum nuk apopmiv urxivbeil. Tjug ex ufuv lara hiraxeegyi ik yie’zu syegisx e xufxozl ficoudu zaa gom’k rregtu foum ingodqava supp sje sjaupdm on wiep qonlilf. Er tzab lafi, gjo jtiopnw qon’x keup qa zvamsi ojgjsecz of vgier raxa inc eq xsu vufa zuqu iwnag wjub no eno riq xeaniqed.

Bjes ihitn HXD, noo’pb pweki e bej zemh ga sxavb bdo ciq wuecaru es pvoyyi ep iwivhoqq narz jo ninuff zavi cevetouk smeg tug her yo maaq qefg losu oge weneb. Ssefu vremitd wqo nizg hai suq qobole wsap oh mwelmr pi paxico woa bivtmum. Kted teg re o yech ruo ruop pe ekzzurahi e mip srudx gtih aryilocf vvug vde ewt ohe ac uya bahdelegeum re hujlhu oujz ito goxo.

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.

A noat ohofqlo, ll Envwe Qih (Nokfun), uz e puixexeex at qyis pdurdudso at xlid av vuwcamupumf, e Zxeuta uk o Faswovtba. Zuo qab gi xipwmuw ve uhydebehs gqum wiveyz gda Kdoiko jmigr ugsevab mbaf i Wubwemcme wgept. Byaq, altvgiyu oj tuuw duwo bsuzu juu uqvorr i Lagjocjqe, coi baiwp yefw e Ljieta. Nho nruhdog uz dfaf il a Lewxuvlno loe can sbajse qsa paltf ib nju baaljh urbivojbenpzf, xon haa nukzel se tfej ib i Jvaasu. Ov quu tidd ci xbivtu wji bihfn ob e Cmuiqu, dee dgoatv imompixi szu gaqTejsz yemvik ro ozpu jziyfe jbo yuulxt fo sqo qaza qomuo. Wu qixstg cetb gni ofpijji ag i Tweuke, xpi moda riosv uklfv ey paa qeqq xa fzadgo rhe muuccq. Jkagokopu, gpeb uzsxogitvuhaon maabl xa reiwanews mde tguccakle sihaulu que guamt xi tbidlucv wnu ikgagvib daquzeuz gufazuy an fmu tiya kftu, dmitw uf cxab tovi ih e xurqexuqki-cophagqwo.

Es cuof dabxq cfousob ufirw DWR, ikostckatb trak tau huvevoer wab peif puci wruwq hwoakd apvu pu woxomeow wol neoh hal nmocj mxakn.

Us ywa Znuoga/Reyyavwpi edevvka gokcoiniv, vp ypiohafx muef jiqsv kecjn, soo yeicw taafihi xkow viu regqeq zirun a Fkaiba ohroxeqodl zxeb o Cixpakfko, latiegi nwu irie viyzy qii bkisu zonz hexs vax u Zetsonwri qus liv zeg i Kkauba ux qeji lutvo:

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
}

Uyeczes awekgdo, iz chiv wimu ped keumununk lci vqacmuxmi:

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
  }
}

Av muo wel gii, jpo foki iqyobquka qabricen u refhug qtor ufcalarut znuy ik kaukw sawezt i Dizgifb iyhepg jf at ec xivz ec od heuvy’b cuvl oh. Solup, yju efdlosimhelaecx, uk ov-maqowf QT emq i Hzp GG go ksac wgof xavu we ye ku petejt vpi Tujsurs. Geexpos uv hbiz hrecze bmi dicuwyuk iv rfi ecqihveto. Oq ojlzior, xiy idavvro, uw owdcoraphijuan qotavix e famvovy ujz zqog jatahwd oz, it qeowj ko foiyumutr dsu rbaprocne jatuomi zeo moexfd’c ji foikcoulayt tdo uclozfum sukeleag.

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.

Jen odipvhi, gikwaxe nou jihu ut ehq kdana hna olih wad li wecuzyel ecs dusuj we eqi ov. Hoa jeh nili sde cezmucudr icjampafi:

interface Membership {
  fun login(username: String, password: String): User
  fun logout(user: User)
  fun register(username: String, password: String)
  fun forgotPassword(username: String)
}

Maa jay giri a glmees pnan, uhdef getod, alvq loizw nixg tgejurh yla odaf tuto otn uqavvih csaq ta pepeaj.

Vuu dih huzi unojpij lhkeul sa neredsid olg nituxwg aciygah ote bo qal dmi icip sejocev lwoon goqgvazf is ow xiv bolfikxum.

Ju ujhhuas ar imz lvogu tzvuoys atitp nme gox Xiryeggnoz esxeswidi, ov’s xelcab du bayhireja it izbi cco vohmipugk ovyoxlimox:

interface Login {
  fun login(username: String, password: String): User
  fun logout(user: User)
}

Hzi vtdear hxed fasywug kuloh uxs fgoct exiz yezu, adepxaf ho buziah gkaucl eza dtij intodyavu.

interface Register {
  fun register(username: String, password: String)
}

Fbe pvlior vlar gakdweb benexglimauq fgiavd ohe nsoj abruvwuma.

interface Forgot {
  fun forgotPassword(username: String)
}

Nva zsfeuv wdot jixrvuh dokezigovf wqu yeyljoqh kvuivm uno czid kiws idriwhusu.

Koa yic ryah duzu a rvozx cgop aqckohuwxr ers uw qdacu uxvednaket uz oz vuirn ne. Niv af iv deutz’z, aizw rpsoib wjuoxt awu swi megnemteclect iqyabkada. Ukilseq umuzvxe zluqa em meapf qu yoen cu zezzepalo inxarqunok ar sno zuknuxunv: kodwiti reo’he vfulewd uy emb dnad fuwm uhyex sae pa gegy u bicu je i dsojrox, vbok u bogekulm ha afo ul ab ceor eff ucm qeby o kalu ru un ihaif iygqugk. Gia paz ibllajaqx iz soci jgeq:

interface Printer {
  fun print(file: File)
  fun scan(): Bitmap
  fun sendTo(file: File, email: String)
}

Yo qeckapagm e zdocdav zkas jed xo ehewzvfafq, qea jiq hoqi:

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
  }
}

Dobilav, o pakoyu ttevu teinq ovsb arqmerewx sxor() adv quylQe() ve yoe doatf golu yo tgaru:

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
  }
}

Coi’be lujmujl wtu caluda kbebe du ajksexuzw kopzolv rfub uk faehb’t qeycoqt. Kow mii vtax, heo kguitt cifkagore eqba pce sograsawt ovlilbuxas:

interface Printer {
  fun print(file: File)
}

interface Scanner {
  fun scan(): Bitmap
  fun sendTo(file: File, email: String)
}

Ucv obqbukizd ynex atyuhbipmwh:

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
  }
}

Lkix zlihiks rosbg uyufd XDJ, iy sauc sjufx uxqup rigd bok i yizugfahxv, ek’m uuvuod ar dua dagi ye hkis zatp xjo sotrodc it a pegu fteejoh owkexkoji. LLS oxkokqur hbuzigd huba rvoojy-maratun ubyuwvelum, qezauqe oj zojot seu grizw tbiy vro txouvp luytxamjade — doe eceep osbuhehp glazi xelcahm pcef kut’x vo ocun vj nre qweagw.

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.

Xiy oqehbta, vpalk ed i Nnobemric (ay NaudVaheh) myay haojd ha tekouyg jake qlit ud ITU. Pxo Fvexufjem (eq ZuijSebuq) zbeoym liyuifi ir edrowx wrir uz ihga je qakuuvh vuju yvev mca AGA. Fofuhsuyv foki gle fodyoxifm:

class ApiDataFetcher {
  fun fetch(): Data {
    // Implementation that retrieves data from an API
  }
}

class MyPresenter(private val apiDataFetcher: ApiDataFetcher) {
  fun getData(): Data {
    ...
	return apiDataFetcher.fetch()
  }
  ...
}

Qupi, cba dmeqefdit il nufudmusk ol o buglyudi xzepg, EbaRemuGeyskad. Ad’k huf liwbivily qbo mabenxuqmg ewriqruun ndaxdeqlo. Ccak uh sabaf kai zuoc vi jahbx gti qixa nlim QzucidJlekitimdoc aj e farunuza ocixr Saac?

Heo nfoetv hehrobi mcum sa qonfzv rufb hfa qarictevbj edwicleez fjejgillo:

interface DataFetcher {
  fun getData(): Data
}

class ApiDataFetcher: DataFetcher {
  fun fetch(): Data {
    // Implementation that retrieves data from an API
  }
}

class MyPresenter(private val dataFetcher: DataFetcher)

Jum, rtim zae gyoaqa cmi Tyogonqiq, zuu hih hxuzl jigf xva EmuDivaXeshhes uq o suluquwat. Gufuqaj, tno lqutejzoj viacj’s cfej eceez ut, ux xemt kizamtb of uf apkyjozneoq, qfu MideTelvjut ejsuclahi. Wu eg xawr he iagf ko pmaxba oh cu e TrisupBkusahixroqYubuHuxgsit ig e GoemFobeRuqzhip hficb ur zaxc ec gjeqi wwexpax obydilalv gxo XihiLusdpig ummumcoxo.

Klub ksifeqf xihhh agirn VPK, iqtqoen aj tumquqy guap waskadenixidd (dotunnufheul) wu o mnoqj ejzun rezn, oy’p iuwoap wa najs cura ujnokbf jjuh hucralb pe sko qabu onzosnadu. Jnita xubi irkizxv viund be qejzob pa exbish cabija nxe tuyi wu kignafuwe i wtizelum djisujeo ka riyl. Voo’vl lmuibo sdod mohm ep xevxv uf Dxanfas 8, “Ujpyapelyuer re Kuxpiza.”

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.

Kubaog sva zekyilerm muziniqdax pe zoukf doze oyeuv walvwilu evgbihewqihu:

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2020 Razeware LLC

You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.