Kas ir programmas arhitektūra. Kāds ir MVC dizaina modelis Java

Labdien, dārgie kolēģi. Šajā rakstā es vēlētos runāt par savu analītisko izpratni par atšķirībām starp MVC, MVP un MVVM modeļiem. Uzrakstīt šo rakstu mani pamudināja vēlme izprast mūsdienu pieejas lielas programmatūras izstrādei un atbilstošās arhitektūras iezīmes. Pašreizējā karjeras kāpņu posmā es neesmu tiešs izstrādātājs, tāpēc rakstā var būt kļūdas, neprecizitātes un pārpratumi. Vai interesējaties par to, kā analītiķi redz programmētāju un arhitektu darbību? Tad laipni lūdzam kaķī.

Saites
Pirmā lieta, ar ko es vēlētos sākt, ir saites uz ārējiem materiāliem, kas mani vadīja šī raksta rakstīšanas procesā:
Ievads
Laikā, kad saule spīdēja spožāk un zāle bija zaļāka, tāda studentu komanda kā šī raksta autors izstrādāja programmatūru, ierakstot simtiem koda rindiņu tieši produkta saskarnē. Dažreiz darbam ar datiem tika izmantoti pakalpojumi un pārvaldnieki, un pēc tam risinājums tika iegūts, izmantojot dokumentu skata modeli. Šāda koda atbalstīšana prasīja milzīgas izmaksas, jo jaunajam izstrādātājam bija jāapmāca (jāpasaka), kurš kods par ko ir atbildīgs produktā, un nebija runas par vienību testēšanu. Izstrādes komanda ir 4 cilvēki, kas sēž vienā telpā.
Gāja laiks, mainījās darbs. Izstrādātās lietojumprogrammas kļuva lielākas un sarežģītākas, un no vienas saliedētas izstrādātāju komandas izveidojās daudz dažādu izstrādātāju, arhitektu, lietojamības speciālistu, dizaineru un PM. Tagad katrs ir atbildīgs par savu jomu: GUI, biznesa loģiku, komponentiem. Parādījās analīzes, testēšanas un arhitektūras nodaļa. Programmatūras izstrādes izmaksas ir pieaugušas simtiem un pat tūkstošiem reižu. Šāda pieeja attīstībai prasa stabilu arhitektūru, kas sinhronizētu dažādas produkta funkcionālās zonas savā starpā.
Raksti
Ņemot vērā mērķi samazināt darbaspēka izmaksas sarežģītas programmatūras izstrādei, mēs pieņemam, ka ir nepieciešams izmantot gatavus vienotus risinājumus. Galu galā veidņu darbības atvieglo saziņu starp izstrādātājiem, ļauj atsaukties uz labi zināmiem dizainiem un samazina kļūdu skaitu.
Saskaņā ar Wikipedia dizaina modelis ir atkārtojams arhitektūras dizains, kas atspoguļo dizaina problēmas risinājumu kādā bieži sastopamā kontekstā.

Sāksim ar pirmo galveno - Model-View-Controller. MVC ir fundamentāls modelis, kas ir atradis ceļu daudzās tehnoloģijās, radījis jaunas tehnoloģijas un katru dienu atvieglo izstrādātāju dzīvi.

MVC modelis pirmo reizi parādījās SmallTalk valodā. Izstrādātājiem bija jānāk klajā ar arhitektūras risinājumu, kas atdalītu grafisko interfeisu no biznesa loģikas un biznesa loģiku no datiem. Tādējādi klasiskajā versijā MVC sastāv no trim daļām, kas tam piešķir nosaukumu. Apskatīsim tos:

Modelis
Modelis parasti tiek saprasts kā daļa, kas satur lietojumprogrammas funkcionālo biznesa loģiku. Modelim jābūt pilnīgi neatkarīgam no pārējā izstrādājuma. Modeļa slānim nekas nav jāzina par dizaina elementiem vai to, kā tas tiks renderēts. Tiek sasniegts rezultāts, kas ļauj mainīt datu prezentāciju, to attēlošanas veidu, nepieskaroties pašam Modelim.

Modelim ir šādas funkcijas:

  • Modelis ir lietojumprogrammas biznesa loģika;
  • Modelim ir zināšanas par sevi un tas nezina par kontrolieriem un skatiem;
  • Dažiem projektiem modelis ir vienkārši datu slānis (DAO, datu bāze, XML fails);
  • Citiem projektiem modelis ir datu bāzes pārvaldnieks, objektu kopa vai vienkārši lietojumprogrammu loģika;
Skatīt
Skata pienākumos ietilpst no Modeļa saņemto datu attēlošana. Tomēr skats nevar tieši ietekmēt modeli. Var teikt, ka skatam ir tikai lasīšanas piekļuve datiem.

Atveidojumam ir šādas īpašības:

  • Skatā tiek realizēta to datu attēlošana, kas tiek iegūti no modeļa jebkādā veidā;
  • Dažos gadījumos skatam var būt kods, kas ievieš kādu biznesa loģiku.
Prezentācijas piemēri: HTML lapa, WPF forma, Windows forma.
Atšķirības starp MVP un MVVM un MVP
Visizplatītākie MVC modeļa veidi ir:
  • Modelis-skats-kontrolieris
  • Modelis-skats-prezentētājs
  • Modelis-skats-skats modelis

Apsvērsim un salīdzināsim katru no tiem.

Modelis-skats-prezentētājs

Šī pieeja ļauj izveidot reprezentācijas abstrakciju. Lai to izdarītu, jums ir jāizvēlas skata saskarne ar noteiktu rekvizītu un metožu kopu. Savukārt prezentētājs saņem atsauci uz saskarnes ieviešanu, abonē prezentācijas pasākumus un pēc pieprasījuma modificē modeli.

Prezentētāja pazīmes:

  • Skats tieši mijiedarbojas ar prezentētāju, izsaucot atbilstošas ​​funkcijas vai notikumus prezentētāja instancē;
  • Prezentētājs mijiedarbojas ar skatu, izmantojot īpašu skata ieviestu saskarni;
  • Viena prezentētāja instance ir saistīta ar vienu displeju.

Īstenošana:
Katram skatam ir jāievieš atbilstošā saskarne. Prezentācijas saskarne nosaka funkciju un notikumu kopu, kas nepieciešamas, lai mijiedarbotos ar lietotāju (piemēram, IView.ShowErrorMessage(string msg)). Prezentētājam jābūt atsaucei uz atbilstošā interfeisa ieviešanu, kas parasti tiek nodota konstruktorā.
Prezentācijas loģikai jābūt atsaucei uz prezentētāja gadījumu. Visi skata notikumi tiek nodoti prezentētājam apstrādei, un prezentācijas loģika tos gandrīz nekad neapstrādā (ieskaitot citu skatu izveidi).

Lietošanas piemērs: Windows veidlapas.

Modelis-skats-skats modelis


Šī pieeja ļauj saistīt skata elementus ar skata modeļa rekvizītiem un notikumiem. Var apgalvot, ka katrs šī raksta slānis nezina par cita slāņa esamību.

View modeļa īpašības:

  • Divvirzienu komunikācija ar prezentāciju;
  • Skata modelis ir skata abstrakcija. Parasti nozīmē, ka skata rekvizīti ir tādi paši kā skata/modeļa rekvizīti
  • Skata modelim nav atsauces uz skata saskarni (IView). Skata modeļa stāvokļa maiņa automātiski maina skatu un otrādi, jo tiek izmantots datu saistīšanas mehānisms (Bindings)
  • Viena skata modeļa instance ir saistīta ar vienu skatu.

Īstenošana:
Izmantojot šo modeli, skats neievieš atbilstošo saskarni (IView).
Skatam jābūt saitei uz datu avotu (DataContex), kas šajā gadījumā ir skata modelis. Skata elementi ir saistīti ar atbilstošajiem skata modeļa rekvizītiem un notikumiem.
Savukārt View modelī ir ieviests īpašs interfeiss, kas tiek izmantots skata elementu automātiskai atjaunināšanai. Šādas saskarnes piemērs WPF varētu būt INotifyPropertyChanged.

Lietošanas piemērs: WPF

Modelis-skats-kontrolieris

Šī modeļa galvenā ideja ir tāda, ka gan kontrolieris, gan skats ir atkarīgi no modeļa, bet modelis nav atkarīgs no šiem diviem komponentiem.

Kontroliera īpašības

  • Kontrolieris nosaka, kurš skats šobrīd ir jāparāda;
  • Skata notikumi var ietekmēt tikai kontrolieri. Kontrolieris var ietekmēt modeli un definēt citu skatu.
  • Vienam kontrollerim ir iespējami vairāki skati;

Īstenošana:
Kontrolieris pārtver notikumu no ārpuses un saskaņā ar tajā iestrādāto loģiku reaģē uz šo notikumu, mainot Modeli, izsaucot atbilstošu metodi. Pēc izmaiņu veikšanas Modelis izmanto notikumu, kuru tas ir mainījis, un visi View notikumi, kas to abonējuši, saņemot to, vēršas pie modeļa atjauninātajiem datiem, pēc kuriem tie tiek parādīti.

Lietošanas piemērs: MVC ASP.NET

Kopsavilkums
MVVM un MVP modeļu ieviešana no pirmā acu uzmetiena izskatās diezgan vienkārša un līdzīga. Tomēr MVVM skata saistīšana ar View-model notiek automātiski, bet MVP ir nepieciešams programmēt
Šķiet, ka MVC vairāk kontrolē skatu.
Vispārīgi noteikumi parauga izvēlei
MVVM
  • Izmanto situācijā, kad iespējama datu saistīšana bez nepieciešamības ieviest īpašas skata saskarnes (t.i., nav nepieciešams ieviest IView);
  • Izplatīts piemērs ir WPF tehnoloģija.
MVP
  • Izmanto situācijā, kad datu iesiešana nav iespējama (Binding nevar izmantot);
  • Izplatīts piemērs būtu Windows veidlapu izmantošana.
MVC
  • Izmanto situācijā, kad nav iespējama saziņa starp skatu un citām lietojumprogrammas daļām (un jūs nevarat izmantot MVVM vai MVP);
  • Izplatīts lietošanas gadījums ir ASP.NET MVC.
Secinājums
Noslēgumā šī raksta autore vēlas norādīt, ka stingri pieturēties pie viena modeļa ne vienmēr ir labākā izvēle. Piemēram, iedomājieties, ka vēlaties izmantot MVVM, lai izstrādātu lietojumprogrammas, izmantojot Windows Forms, izmantojot vadīklu rekvizītu Bindings. Jūsu mērķis ir atdalīt prezentāciju no biznesa loģikas un loģikas, kas tos savieno. Lietojumprogrammai jābūt viegli pārbaudāmai un atbalstāmai, kā arī analītiķiem saprotamai (galu galā uz jautājumu “kā tiek mērīta cietā diska veiktspēja” ir tikai viena pareizā atbilde - džoulos (abstrakts piemērs Modelis -> Skati)) .

Liels paldies par veltīto laiku, patīkamu lasīšanu!

Model-View-Controller (MVC) modelis ir ļoti noderīgs, veidojot lietojumprogrammas ar sarežģītu GUI vai uzvedību. Bet tas ir piemērots arī vienkāršākiem gadījumiem. Šajā ierakstā mēs izveidosim mīnu meklētāju spēli, kas izstrādāta, pamatojoties uz šo modeli. Python tika izvēlēts kā izstrādes valoda, taču tas nav īpaši svarīgi. Modeļi nav atkarīgi no konkrētas programmēšanas valodas, un jūs varat viegli pārsūtīt iegūto ieviešanu uz jebkuru citu platformu.

Reklāma

Īsi par MVC modeli

Kā norāda nosaukums, MVC modelī ir iekļauti 3 komponenti: modelis, skats un kontrolieris. Katra no sastāvdaļām pilda savu lomu un ir savstarpēji aizvietojama. Tas nozīmē, ka komponenti ir savienoti viens ar otru tikai ar noteiktiem skaidriem interfeisiem, aiz kuriem var slēpties jebkura ieviešana. Šī pieeja ļauj nomainīt un apvienot dažādus komponentus, nodrošinot nepieciešamo darbības loģiku vai lietojumprogrammas izskatu. Apskatīsim funkcijas, kuras veic katrs komponents.

Modelis

Atbildīgs par programmas iekšējo loģiku. Šeit mēs varam paslēpt datu uzglabāšanas metodes, kā arī informācijas apstrādes noteikumus un algoritmus.

Piemēram, vienai lietojumprogrammai varam izveidot vairākus modeļus. Viens tiks atkļūdots, bet otrs darbosies. Pirmais var saglabāt savus datus atmiņā vai failā, bet otrais jau izmanto datu bāzi. Būtībā tas ir tikai stratēģijas modelis.

Performance

Atbildīgs par modeļa datu parādīšanu. Šajā līmenī mēs nodrošinām tikai saskarni lietotāja mijiedarbībai ar modeli. Šī komponenta ieviešanas jēga ir tāda pati kā gadījumā, ja tiek nodrošināti dažādi datu glabāšanas veidi, kuru pamatā ir vairāki modeļi.

Piemēram, izstrādes sākumposmā mēs savai lietojumprogrammai varam izveidot vienkāršu konsoles skatu un tikai pēc tam pievienot skaisti noformētu GUI. Turklāt joprojām ir iespējams saglabāt abu veidu saskarnes.

Turklāt jāņem vērā, ka View pienākumos ietilpst tikai savlaicīga Modeļa stāvokļa attēlošana. Pārzinis ir atbildīgs par lietotāja darbību apstrādi, par ko mēs tagad runāsim.

Kontrolieris

Nodrošina saiti starp modeli un lietotāja darbībām, kas izriet no mijiedarbības ar skatu. Saskaņo Modeļa un Skata stāvokļu atjaunināšanas brīžus. Pieņem lielāko daļu lēmumu par lietojumprogrammu pāreju no viena stāvokļa uz citu.

Faktiski katrai darbībai, ko lietotājs var veikt skatā, kontrolierī ir jādefinē apstrādātājs. Šis apstrādātājs veiks atbilstošās manipulācijas ar modeli un, ja nepieciešams, paziņos View, ka ir notikušas izmaiņas.

Reklāma

Minesweeper spēles specifikācijas

Pietiek teorijas. Tagad pāriesim pie prakses. Lai demonstrētu MVC modeli, mēs uzrakstīsim vienkāršu spēli: Minesweeper. Spēles noteikumi ir pavisam vienkārši:

  1. Spēles laukums ir taisnstūrveida laukums, kas sastāv no šūnām. Dažās šūnās ir nejauši novietotas mīnas, bet spēlētājs par tām nezina;
  2. Spēlētājs var noklikšķināt uz jebkuras spēles lauka šūnas ar kreiso vai labo peles pogu;
  3. Noklikšķinot ar peles kreiso pogu, šūna tiek atvērta. Turklāt, ja šūnā ir mīna, tad spēle beidzas ar zaudējumu. Ja blakus esošajās šūnās blakus atvērtai šūnai ir mīnas, tad atvērtajā šūnā tiks parādīts skaitītājs ar apkārt esošo mīnu skaitu. Ja ap atvērto šūnu nav mīnu, tad katra blakus esošā šūna būs atvērta pēc tāda paša principa. Tas ir, šūnas tiks atvērtas, līdz tās vai nu sasniegs spēles laukuma robežu, vai sasniegs jau atvērtas šūnas, vai arī tām blakus nav raktuves;
  4. Noklikšķinot ar peles labo pogu, varat atzīmēt šūnas. Noklikšķinot uz aizvērtas šūnas, tā tiek atzīmēta ar karodziņu, kas bloķē tās stāvokli un novērš nejaušu atvēršanu. Noklikšķinot uz lodziņa, kas atzīmēta ar karodziņu, tā zīme tiek mainīta uz jautājuma zīmi. Šajā gadījumā šūna vairs nav bloķēta un to var atvērt ar peles kreiso pogu. Noklikšķinot uz šūnas ar jautājuma zīmi, tā tiek atgriezta aizvērtā, neatzīmētajā stāvoklī;
  5. Uzvaru nosaka spēles stāvoklis, kurā visas šūnas spēles laukumā ir atvērtas, izņemot mīnētās.

Tālāk ir sniegts piemērs tam, ko mēs iegūsim:

Spēles Minesweeper UML diagrammas

Pirms pāriet uz koda rakstīšanu, būtu ieteicams iepriekš pārdomāt lietojumprogrammas arhitektūru. Tam nevajadzētu būt atkarīgam no ieviešanas valodas, tāpēc UML ir vislabākais mūsu mērķiem.

Spēles šūnu stāvokļa diagramma

Jebkurai šūnai spēles laukā var būt kāds no 4 stāvokļiem:

  1. Būris ir slēgts;
  2. Būris ir atvērts;
  3. Šūna ir atzīmēta ar karogu;
  4. Šūna ir atzīmēta ar jautājuma zīmi.

Šeit mēs esam definējuši tikai pārstāvniecībai nozīmīgos stāvokļus. Tā kā spēles laikā mīnas netiek rādītas, atbilstošais stāvoklis nav paredzēts pamatkomplektā. Definēsim iespējamās pārejas no vienas šūnas stāvokļa uz citu, izmantojot UML stāvokļa diagrammas:

Mīnu kuģa spēles klases diagramma

Tā kā mēs nolēmām izveidot savu lietojumprogrammu, pamatojoties uz MVC modeli, mums būs trīs galvenās klases: MinesweeperModel, MinesweeperView un MinesweeperController, kā arī palīgklase MinesweeperCell, lai saglabātu šūnas stāvokli. Apskatīsim viņu klases diagrammu:

Arhitektūras organizācija ir diezgan vienkārša. Šeit mēs esam vienkārši sadalījuši uzdevumus katrai klasei saskaņā ar MVC modeļa principiem:

  1. Hierarhijas pašā apakšā ir spēļu šūnu klase MinesweeperCell. Tas saglabā šūnas pozīciju, ko nosaka spēles lauka rindas rinda un kolonnas kolonna; viens no stāvokļiem, ko mēs aprakstījām iepriekšējā apakšnodaļā; informācija par mīnu klātbūtni šūnā (mīnu) un mīnu skaitītāju blakus šūnās skaitītājs . Turklāt tam ir divas metodes: nextMark(), lai pārvietotos pa stāvokļiem, kas saistīti ar atzīmēm, kas izriet no labās pogas klikšķa, un open(), kas apstrādā ar kreisās pogas klikšķi;
  2. Tieši augšā ir MinesweeperModel Model klase. Viņš ir MinesweeperCell spēļu šūnu konteiners. Tās pirmā metode startGame() sagatavo spēles laukumu spēles sākumam. Metode isWin() pārbauda, ​​vai spēles laukā ir uzvaras statuss, un atgriež vērtību True, ja spēlētājs uzvar, pretējā gadījumā tā atgriež false. Lai pārbaudītu zaudējumus, tiek izmantota līdzīga metode isGameOver(). OpenCell() un nextCellMark() metodes vienkārši deleģē darbības attiecīgajām spēles lauka šūnām, un metode getCell() atgriež pieprasīto atskaņošanas šūnu;
  3. Klase MinesweeperView View ietver šādas metodes: syncWithModel() - nodrošina Skata pārzīmēšanu, lai parādītu pašreizējo spēles lauka stāvokli modelī; getGameSettings() - atgriež lietotāja norādītos spēles iestatījumus; createBoard() - izveido spēles laukumu, pamatojoties uz modeļa datiem; showWinMessage() un showGameOverMessage() attiecīgi parāda uzvaras un zaudējuma ziņojumus;
  4. Un visbeidzot Controller klases MinesweeperController. Tas nosaka tikai trīs metodes katrai iespējamai spēlētāja darbībai: startNewGame() ir atbildīgs par noklikšķināšanu uz pogas “Jauna spēle” skata saskarnē; onLeftClick() un onRightClick() apstrādā klikšķus uz spēles šūnām, attiecīgi izmantojot kreiso un labo peles pogu.

Spēles Minesweeper ieviešana Python

Ir pienācis laiks sākt īstenot mūsu projektu. Izvēlēsimies Python kā izstrādes valodu. Pēc tam mēs rakstīsim View klasi, pamatojoties uz tkinter moduli.

Bet sāksim ar modeli.

Modelis MinsweeperModel

Modeļa Python ieviešana izskatās šādi:

MIN_ROW_COUNT = 5 MAX_ROW_COUNT = 30 MIN_COLUMN_COUNT = 5 MAX_COLUMN_COUNT = 5 MAX_COLUMN_COUNT = 30 MIN_MINE_COUNT = 1 MAX_MINE_COUNT = 800 klase MinesweeperCell: # Iespējamie spēles šūnas stāvokļi: # jautājums aizvērts - aizvērts # karodziņš __init__(self , rinda, kolonna): self.row = rinda self.column = kolonna self.state = "slēgts" self.mined = viltus self.counter = 0 markSequence = [ "slēgts", "atzīmēts ar karodziņu", "apšaubīts" ] def nextMark (self): if self.state in self.markSequence: stateIndex = self.markSequence.index(self.state) self.state = self.markSequence[ (stateIndex + 1) % len(self.markSequence) ] def open(self): if self.state != "flagged": self.state = "atvērta" klase MinesweeperModel: def __init__(self): self.startGame() def startGame(self, rowCount = 15, kolonnu skaits = 15, mineCount = 15) : ja rindu skaits diapazonā (MIN_ROW_COUNT, MAX_ROW_COUNT + 1): self.rowCount = rindu skaits, ja kolonnu skaits diapazonā (MIN_COLUMN_COUNT, MAX_COLUMN_COUNT + 1): self.columnCount = kolonnu skaits, ja mineCount< self.rowCount * self.columnCount: if mineCount in range(MIN_MINE_COUNT, MAX_MINE_COUNT + 1): self.mineCount = mineCount else: self.mineCount = self.rowCount * self.columnCount - 1 self.firstStep = True self.gameOver = False self.cellsTable = for row in range(self.rowCount): cellsRow = for column in range(self.columnCount): cellsRow.append(MinesweeperCell(row, column)) self.cellsTable.append(cellsRow) def getCell(self, row, column): if row < 0 or column < 0 or self.rowCount <= row or self.columnCount <= column: return None return self.cellsTable[ row ][ column ] def isWin(self): for row in range(self.rowCount): for column in range(self.columnCount): cell = self.cellsTable[ row ][ column ] if not cell.mined and (cell.state != "opened" and cell.state != "flagged"): return False return True def isGameOver(self): return self.gameOver def openCell(self, row, column): cell = self.getCell(row, column) if not cell: return cell.open() if cell.mined: self.gameOver = True return if self.firstStep: self.firstStep = False self.generateMines() cell.counter = self.countMinesAroundCell(row, column) if cell.counter == 0: neighbours = self.getCellNeighbours(row, column) for n in neighbours: if n.state == "closed": self.openCell(n.row, n.column) def nextCellMark(self, row, column): cell = self.getCell(row, column) if cell: cell.nextMark() def generateMines(self): for i in range(self.mineCount): while True: row = random.randint(0, self.rowCount - 1) column = random.randint(0, self.columnCount - 1) cell = self.getCell(row, column) if not cell.state == "opened" and not cell.mined: cell.mined = True break def countMinesAroundCell(self, row, column): neighbours = self.getCellNeighbours(row, column) return sum(1 for n in neighbours if n.mined) def getCellNeighbours(self, row, column): neighbours = for r in range(row - 1, row + 2): neighbours.append(self.getCell(r, column - 1)) if r != row: neighbours.append(self.getCell(r, column)) neighbours.append(self.getCell(r, column + 1)) return filter(lambda n: n is not None, neighbours)

Augšpusē mēs definējam pieņemamo spēles iestatījumu diapazonu:

MIN_ROW_COUNT = 5 MAX_ROW_COUNT = 30 MIN_COLUMN_COUNT = 5 MAX_COLUMN_COUNT = 30 MIN_MINE_COUNT = 1 MAX_MINE_COUNT = 800

Kopumā šos iestatījumus varētu arī padarīt par daļu no modeļa. Tomēr lauka lielums un mīnu skaits ir diezgan statiska informācija un, visticamāk, nemainīsies bieži.

Tālāk mēs definējām spēles šūnu klasi MinesweeperCell. Tas izrādījās pavisam vienkārši. Klases konstruktorā šūnu lauki tiek inicializēti ar noklusējuma vērtībām. Tālāk, lai vienkāršotu ciklisko stāvokļu pāreju ieviešanu, mēs izmantojam markSequence palīgsarakstu. Ja šūna atrodas "atvērtā" stāvoklī, kas nav iekļauts šajā sarakstā, tad nextMark() metodē nekas nenotiks, pretējā gadījumā šūna pāriet uz nākamo stāvokli, un no pēdējā "jautātā" stāvokļa tā "lec "uz sākotnējo "slēgto" stāvokli". Open() metodē mēs pārbaudām šūnas stāvokli, un, ja tā nav "atzīmēta", tad šūna pāriet atvērtā stāvoklī "atvērta".

Tālāk seko MinesweeperModel Model klases definīcija. Metode startGame() izkārto spēles laukumu, izmantojot tam nodotos parametrus rowCount , columnCount un mineCount. Katrs parametrs tiek pārbaudīts, lai redzētu, vai tas ir pieņemamā vērtību diapazonā. Ja pārraidītā vērtība ir ārpus diapazona, tad spēles lauka parametra vērtība tiek saglabāta un nemainās. Jāpiebilst, ka papildus tiek pārbaudīts mīnu skaits. Ja pārsūtītais mīnu skaits pārsniedz lauka lielumu, tad ierobežojam to līdz šūnu skaitam bez vienības. Lai gan, protams, šādai spēlei nav lielas jēgas un tā tiks pabeigta vienā solī, tāpēc jūs varat izdomāt dažus savus noteikumus šādam gadījumam.

Spēles lauks tiek saglabāts kā šūnu sarakstu saraksts cellTable mainīgajā. Turklāt, lūdzu, ņemiet vērā, ka startGame() metodē ir iestatīta tikai šūnu pozīcijas vērtība, bet mīnas vēl nav novietotas. Bet mainīgais firstStep ir definēts ar vērtību True . Tas ir nepieciešams, lai no pirmā gājiena noņemtu nejaušības elementu un novērstu tūlītēju zaudējumu. Mīnas tiks novietotas pēc pirmās kustības atlikušajās šūnās.

Metode getCell () vienkārši atgriež spēles lauka šūnu pa rindas rindām un kolonnas kolonnām. Ja rindas vai kolonnas vērtība nav derīga, tiek atgriezts None.

Metode isWin() atgriež vērtību True, ja visas atlikušās spēles laukuma neatvērtās šūnas ir mīnētas, tas ir, uzvaras gadījumā, pretējā gadījumā tā atgriezīs False. Metode isGameOver() vienkārši atgriež gameOver klases atribūta vērtību.

OpenCell() metode deleģē open() izsaukumu spēles šūnas objektam, kas atrodas spēles laukumā metodes parametros norādītajā vietā. Ja izrādās, ka atvērtā šūna ir mīnēta, mēs iestatām gameOver vērtību uz True un izejam no metodes. Ja spēle vēl nav beigusies, pārbaudām firstStep vērtību, lai noskaidrotu, vai šis ir pirmais gājiens. Ja tas tiešām ir pirmais gājiens, tad mīnas tiks novietotas spēles laukumā, izmantojot generMines() palīgmetodi, par kuru mēs runāsim nedaudz vēlāk. Tālāk mēs saskaitām iegūto blakus šūnu skaitu un iestatām atbilstošo skaitītāja atribūta vērtību apstrādājamai šūnai. Ja skaitītājs ir nulle, mēs pieprasām blakus esošo šūnu sarakstu, izmantojot metodi getCellNeighbours () un rekursīvi izsaucam metodi openCell () visiem slēgtajiem "kaimiņiem", tas ir, šūnām ar statusu "slēgts".

NextCellMark() metode vienkārši deleģē izsaukumu nextMark() metodei šūnai, kas atrodas nodotajā pozīcijā.

Mīnu izvietošana notiek, izmantojot metodi generateMines(). Šeit mēs vienkārši nejauši izvēlamies pozīciju spēles laukā un pārbaudām, vai šūna šajā pozīcijā nav atvērta un jau nav iegūta. Ja abi nosacījumi ir izpildīti, mēs iestatām iegūtā atribūta vērtību uz True, pretējā gadījumā mēs turpinām meklēt citu brīvu šūnu. Neaizmirstiet, ka, lai Python izmantotu izlases moduli, tas ir skaidri jāimportē, izmantojot importēšanas izlases komandu.

Metode countMinesAroundCell() mīnu skaita skaitīšanai ap noteiktu spēles lauka šūnu ir pilnībā balstīta uz getCellNeighbours() metodi. Šūnas "kaimiņu" pieprasīšana ar metodi getCellNeighbours() arī tiek īstenota ārkārtīgi vienkārši. Es nedomāju, ka jums ar to būs problēmas.

MinesweeperView

Tagad ķersimies pie izrādes. Tālāk ir parādīts Python klases MinesweeperView kods:

Klase MinesweeperView(Frame): def __init__(self, model, controller, parent = Nav): Frame.__init__(self, parent) self.model = modelis self.controller = kontrolieris self.controller.setView(self) self.createBoard( ) panelis = Frame(self) panel.pack(side = BOTTOM, fill = X) Poga(panelis, teksts = "Jauna spēle", komanda = self.controller.startNewGame).pack(side = RIGHT) self.mineCount = StringVar (panelis) self.mineCount.set(self.model.mineCount) Spinbox(panelis, no_ = MIN_RANU_SKAITS, līdz = MAKSIM_MINE_SKAITS, teksta mainīgais = self.mineCount, platums = 5).pack(side = RIGHT) Etiķete(panelis, teksts = " Minūšu skaits: ").pack(side = RIGHT) self.rowCount = StringVar(panel) self.rowCount.set(self.model.rowCount) Spinbox(panel, from_ = MIN_ROW_COUNT, to = MAX_ROW_COUNT, teksta mainīgais = self. rindu skaits , platums = 5).pack(side = RIGHT) Iezīme(panelis, teksts = " x ").pack(side = RIGHT) self.columnCount = StringVar(panel) self.columnCount.set(self.model.columnCount) Spinbox (panelis, no_ = MIN_COLUMN_COUNT, līdz = MAX_COLUMN_COUNT, teksta mainīgais = self.columnCount, platums = 5).pack(side = RIGHT) Label(panel, text = "Lauka izmērs: ").pack(side = RIGHT) def syncWithModel ( self): rindai diapazonā(self.model.rowCount): kolonnai diapazonā(self.model.columnCount): cell = self.model.getCell(rinda, kolonna) if cell: btn = self.buttonsTable[ row ] [ kolonna ] if self.model.isGameOver() un cell.mined: btn.config(bg = "melns", teksts = "") if cell.state == "closed": btn.config(text = "" ) elif cell.state == "atvērts": btn.config(relief = SUNKEN, text = "") if cell.counter > 0: btn.config(text = cell.counter) elif cell.mined: btn.config( bg = "sarkans") elif cell.state == "atzīmēts": btn.config(teksts = "P") elif cell.state == "apšaubīts": btn.config(text = "?") def blockCell(self , rinda, kolonna, bloks = True): btn = self.buttonsTabula[ rinda ][ kolonna ] ja nav btn: return if block: btn.bind(" ", "break") else: btn.unbind(" ") def getGameSettings(self): return self.rowCount.get(), self.columnCount.get(), self.mineCount.get() def createBoard(self): try: self.board.pack_forget() self.board .destroy() self.rowCount.set(self.model.rowCount) self.columnCount.set(self.model.columnCount) self.mineCount.set(self.model.mineCount), izņemot: nodot self.board = Frame(self ) self.board.pack() self.buttonsTable = rindai diapazonā(self.model.rowCount): line = Frame(self.board) line.pack(side = TOP) self.buttonsRow = kolonnai diapazonā(self .model.columnCount): btn = poga (līnija, platums = 2, augstums = 1, komanda = lambda rinda = rinda, kolonna = kolonna: self.controller.onLeftClick(rinda, kolonna), padx = 0, pady = 0) btn.pack(side = LEFT) btn.bind(" ", lambda e, rinda = rinda, kolonna = kolonna: self.controller.onRightClick(rinda, kolonna)) self.buttonsRow.append(btn) self.buttonsTable.append(self.buttonsRow) def showWinMessage(self): showinfo( "Apsveicam!", "Tu uzvar!") def showGameOverMessage(self): showinfo ("Spēle beigusies!", "Tu zaudē!")

Mūsu skats ir balstīts uz Frame klasi no tkinter moduļa, tāpēc noteikti izpildiet atbilstošo importēšanas komandu: from tkinter import * . Klases konstruktors nokārto modeli un kontrolieri. Nekavējoties tiek izsaukta metode createBoard(), lai izkārtotu spēles laukumu no šūnām. Jau iepriekš pateikšu, ka šim nolūkam izmantosim parastās Button pogas. Pēc tam tiek izveidots rāmis, kas darbosies kā apakšējais panelis spēles parametru norādīšanai. Šajā panelī mēs secīgi ievietojam pogu “Jauna spēle”, kuras apstrādātājs ir mūsu Kontrolieris ar startNewGame() metodi, un pēc tam trīs Spinbox skaitītājus, lai spēlētājs varētu norādīt spēles laukuma lielumu un mīnu skaitu.

Metode syncWithModel() vienkārši divreiz iziet cauri katrai spēles šūnai un attiecīgi maina pogas izskatu, kas to attēlo mūsu GUI. Vienkāršības labad es izmantoju teksta simbolus, lai parādītu apzīmējumu, taču nav tik grūti mainīt tekstu uz grafiku no ārējiem grafikas failiem.

Ņemiet vērā arī to, ka mēs izmantojam pogu SUNKEN stilu, lai attēlotu atvērtu šūnu. Un zaudējuma gadījumā mēs atveram visu mīnu atrašanās vietu spēles laukā, parādot atbilstošās pogas melnā krāsā, un sarkanā krāsā iezīmējam pogu, kas atbilst pēdējai atvērtajai šūnai ar mīnu:

Tālāk norādītā metode blockCell() kalpo atbalsta lomai un ļauj kontrollerim iestatīt pogu bloķēšanas stāvokli. Tas ir paredzēts, lai novērstu nejaušu spēļu šūnu atvēršanu ar karodziņu, un tas tiek panākts, iestatot tukšu kreisās puses klikšķa apdarinātāju.

GetGameSettings() metode vienkārši atgriež apakšējā panelī esošo skaitītāju vērtības, norādot spēles laukuma lielumu un mīnu skaitu.

Spēles lauka attēlojuma izveide tiek veikta, izmantojot metodi createBoard(). Pirmkārt, mēs cenšamies izdzēst veco spēles laukumu, ja tāds pastāvēja, kā arī cenšamies iestatīt skaitītāja vērtības no paneļa saskaņā ar pašreizējo modeļa konfigurāciju. Pēc tam tiek izveidots jauns rāmis, ko mēs sauksim par dēli, lai attēlotu spēles laukumu. Pogu tabulu tabulu veidojam pēc tāda paša principa kā modeļa spēļu šūnas, izmantojot dubulto cilpu. Katras pogas apstrādātāji ir saistīti ar kontroliera onLeftClick() un onRightClick() metodēm, lai attiecīgi noklikšķinātu uz peles kreisās un labās pogas.

Pēdējās divas metodes showWinMessage() un showGameOverMessage() vienkārši parāda dialoglodziņus ar atbilstošajiem ziņojumiem, izmantojot funkciju showinfo(). Lai to izmantotu, jums būs jāimportē vēl viens modulis: no tkinter.messagebox import * .

Kontrolieris MinesweeperController

Tagad esam nonākuši pie kontroliera ieviešanas:

Class MinesweeperController: def __init__(self, model): self.model = model def setView(self, view): self.view = view def startNewGame(self): gameSettings = self.view.getGameSettings() try: self.model. startGame(*map(int, gameSettings)), izņemot: self.model.startGame(self.model.rowCount, self.model.columnCount, self.model.mineCount) self.view.createBoard() def onLeftClick(self, row, kolonna): self.model.openCell(rinda, kolonna) self.view.syncWithModel() if self.model.isWin(): self.view.showWinMessage() self.startNewGame() elif self.model.isGameOver(): self.view.showGameOverMessage() self.startNewGame() def onRightClick(self, rinda, kolonna): self.model.nextCellMark(rinda, kolonna) self.view.blockCell(rinda, kolonna, self.model.getCell(rinda, kolonna).state == "atzīmēts") self.view.syncWithModel()

Lai saistītu skatu ar kontrolieri, mēs pievienojām metodi setView(). Tas ir tāpēc, ka, ja mēs vēlamies nodot skatu konstruktoram, šim skatam vajadzētu pastāvēt jau pirms kontroliera izveides. Un tad līdzīgs risinājums ar papildu saistīšanas metodi vienkārši pārvietotos no Controller uz skatu, kurā parādītos metode setController().

Apdarinātāja metode, lai noklikšķinātu uz pogas Jauna spēle, startNewGame(), vispirms pieprasa skatā ievadītos spēles parametrus. Spēles parametri tiek atgriezti kā trīs komponentu kopa, ko mēs cenšamies pārvērst par int . Ja viss norit labi, mēs šīs vērtības nododam modeļa startGame() metodei, lai izveidotu spēles laukumu. Ja kaut kas noiet greizi, mēs vienkārši atjaunosim spēles laukumu ar vecajiem parametriem. Visbeidzot, mēs pieprasām izveidot jaunu spēles dēļa displeju skatā, izsaucot metodi createBoard().

Apdarinātājs onLeftClick() vispirms liek modelim atvērt spēles šūnu spēlētāja izvēlētajā pozīcijā. Tad tā informē Skatu, ka Modeļa stāvoklis ir mainījies un piedāvā visu pārzīmēt. Pēc tam modelim tiek pārbaudīta uzvara vai zaudējums. Ja kaut kas no tā notiek, vispirms uz skatu tiek nosūtīts pieprasījums, lai parādītu atbilstošo paziņojumu, un pēc tam tiek izsaukts startNewGame() apdarinātājs, lai sāktu jaunu spēli.

Ar peles labo pogu noklikšķiniet uz onRightClick() metodes. Pirmajā rindā tiek izsaukta modeļa nextCellMark() metode, lai cikliski mainītu atlasītās spēles šūnas atzīmi. Atkarībā no šūnas jaunā stāvokļa uz skatu tiek nosūtīts pieprasījums iestatīt vai noņemt attiecīgās pogas bloķēšanu. Beigās skats atkal tiek atjaunināts, lai parādītu modeļa pašreizējo stāvokli.

Apvienojot modeli, skatu un kontrolieri

Tagad atliek tikai savienot visus elementus mūsu Minesweeper ieviešanā, pamatojoties uz MVC modeli, un palaist spēli:

Modelis = MinesweeperModel() kontrolieris = MinesweeperController(modelis); skats = MinesweeperView(modelis, kontrolleris) view.pack() view.mainloop()

Secinājums

Tāpēc mēs apskatījām MVC modeli. Īsi apskatīsim teoriju. Un tad mēs soli pa solim izveidojām pilnvērtīgu spēļu lietojumprogrammu, sākot no problēmas izklāsta un arhitektūras dizaina līdz ieviešanai Python programmēšanas valodā, izmantojot grafisko moduli tkinter.

Visā interneta pasaulē ir izkaisīti miljoniem tīmekļa lietojumprogrammu. Ir daži, kas ir diezgan vienkārši, un ir tādi, kuriem "pats matricas arhitekts salauztu kāju". Bet viņiem ir viena kopīga iezīme - MVC.

Vispopulārākais arhitektūras modelis pasaulē starp tīmekļa lietojumprogrammām - model-view-controller (Model View Controller vai vienkārši MVC). Pirmo reizi tas tika izmantots divdesmitā gadsimta 70. gadu beigās lietojumprogrammās šajā valodā. Smalltalk. Un tad programmētāji viņu uzņēma Java un kopīgoja to ar visu pasauli un visām programmēšanas valodām. PHP nebija izņēmums. Mūsdienās tikai neliela daļa programmētāju, kas savāc retu PHP kodu, var atļauties neskatīties uz MVC.

Tas kļuva tik populārs kāda iemesla dēļ. Tas vienkārši tika radīts, lai izveidotu elastīgas un mērogojamas lietojumprogrammas, kuras ir viegli uzturēt un paplašināt.
Mūsu apmācības mērķis ir ar vienkāršu piemēru parādīt, kā darbojas MVC modelis.

Lai izpildītu uzdevumus, jums būs nepieciešamas šādas programmas:

Piezīmes:

  • Mēs pieņemam, ka jums ir pamatzināšanas par PHP.

MVC modelis

Tagad parunāsim par visu kārtībā. Pirmkārt, atklāsim lielo saīsinājuma noslēpumu, kas acīmredzami atspoguļo faktu, ka lietojumprogramma sastāvēs no trim savstarpēji mijiedarbīgām daļām:

  • Modelis ir atbildīgs par datu pārvaldību, tas saglabā un izgūst lietojumprogrammas izmantotās entītijas, parasti no datu bāzes un satur lietojumprogrammā ieviesto loģiku.
  • Performance ir atbildīgs par pārziņa sniegto datu attēlošanu. Ar prezentāciju cieši saistīta ir veidnes koncepcija, kas ļauj mainīt parādītās informācijas izskatu. Tīmekļa lietojumprogrammā skats bieži tiek ieviests kā HTML lapa.
  • Kontrolieris savieno modeli un skatu. Tas saņem pieprasījumu no klienta, analizē tā parametrus un piekļūst modelim, lai veiktu darbības ar pieprasījuma datiem. No modeļa nāk jau salikti objekti. Pēc tam tie tiek novirzīti uz skatu, kas ģenerēto lapu nodod kontrolierim, kas savukārt nosūta to klientam.

Datu plūsmas šajā modelī var shematiski attēlot šādi:

Ieiešana realitātē

Beidzot formulēsim patieso problēmu. Pieņemsim, ka viņi mums lika izveidot sociālo tīklu vietni. Šim gigantiskajam uzdevumam ir neliels apakšuzdevums: izmantojot esošo draugu datu bāzi, nodrošināt viņu pilnā saraksta apskati, kā arī detalizētu informāciju par katru draugu.

Mēs tagad neņemsim vērā visa sociālā tīkla arhitektūru. Mēs veiksim tikai nelielu apakšuzdevumu, iedomāsimies tā nopietnību un piemērosim tam MVC modeli.

Tiklīdz mēs sākam to lietot, mēs uzreiz domājam - kā mēs varam sakārtot mūsu risinājuma skriptus, lai viss būtu pa rokai? Lai to izdarītu, ievietosim katru no trim mūsu MVC sistēmas sadaļām atsevišķās mapēs un tādējādi iegūsim vienkāršu direktoriju struktūru, kurā ir viegli atrast vajadzīgo. Turklāt mēs ievietosim šīs trīs mapes lib direktorijā un pārvietosim to virs saknes www tīmekļa direktorija:

/lib --/controller ---- FrendCnt.php --/model ---- Frend.php ---- FrendList.php --/view ---- frendlist.php ---- frendone.php / www -- index.php -- .htaccess

Kataloga augšupielāde lib(kas satur mūsu vietnes dzinēju) no tīmekļa direktorijas sniedz mums lielāku drošību, padarot mūsu sistēmu nepieejamu hakeru rotaļīgo roku iejaukšanās.

Kontrolieris

Tagad parunāsim par visu kārtībā. Sāksim ar kontrolieris, jo tā ir pirmā no trim modeļa sastāvdaļām, kas atbilst klienta pieprasījumam, parsē to elementos un inicializē modeļa objektus. Pēc datu apstrādes modelī tas pieņem tā atbildi un nosūta to uz prezentācijas slāni.

Mūsu vienkāršajā piemērā kontrolieris tiks koncentrēts vienā FrendCnt klasē. Mēs to sīkāk aprakstīsim vēlāk, un tagad nedaudz par ieejas punktu tīmekļa lietojumprogrammā - tas, protams, būs fails indekss.php. Tajā mēs noteiksim sākumpunktu mūsu skriptu savienošanai. Izveidosim kontroliera gadījumu un izsauksim tajā metodi, kas sāks HTTP pieprasījuma apstrādi un noteiks, kā rīkoties tālāk.

Saraksts Nr. 1 (fails index.php):

$baseDir = dirname(__FILE__) . "/.."; include_once($baseDir . "/lib/controller/FriendCnt.php"); $kontrolleris = jauns FriendCnt(); $kontrolleris->izsaukt();

Tagad par kontrolieri. Mums šī ir FriendCnt klase. Jūs jau pamanījāt, ka šīs klases gadījums ir izveidots indekss.php. Tam ir tikai viena invoke() metode, kas tiek izsaukta tūlīt pēc instances izveides. Kontroliera konstruktorā tiek izveidots objekts, pamatojoties uz modeļa klasi - FrendList (draugu saraksts) darbam ar datiem.

Funkcijā invoke(), pamatojoties uz ienākošo HTTP pieprasījumu, tiek pieņemts lēmums: kādi dati ir nepieciešami no modeļa. Pēc tam tiek izsaukta metode, kas izgūst datus. Tālāk tiek pievienotas displeja veidnes, uz kurām tiek pārsūtīti dati no kontrollera. Ņemiet vērā, ka kontrolieris neko nezina par datu bāzi vai to, kā lapa tiek renderēta.

Saraksts Nr. 2 (kontrollera fails FriendCnt.php):

Require_once($baseDir . "/lib/model/FriendList.php"); class FriendCnt ( public $oFriendList; publiskā funkcija __construct() ( $this->oFriendList = new FriendList(); ) publiskā funkcija invoke() ( globālā $baseDir; $oFriendList = $this->oFriendList; if(isset($_GET) ["atslēga"])) ($oFriendList->setKey($_GET["atslēga"]); $oFriend = $oFriendList->fetch(); ietver $baseDir . "/lib/view/friendone.php"; ) else ( $aFriend = $oFriendList->fetch(); ietver $baseDir . "/lib/view/friendlist.php"; ) )

Modelis un entītijas

Modelis- tas ir realitātes attēls, no kura tiek ņemts tikai tas, kas nepieciešams problēmas risināšanai. Modelis koncentrējas uz galvenās problēmas risināšanas loģiku. Daudzi to sauc par biznesa loģiku, uz to attiecas liela atbildība:

  • Lietojumprogrammu datu saglabāšana, dzēšana, atjaunināšana. Tas tiek īstenots, izmantojot datu bāzes darbības vai izsaukumus uz ārējiem tīmekļa pakalpojumiem.
  • Visas lietojumprogrammu loģikas iekapsulēšana. Pilnīgi visa lietojuma loģika bez izņēmuma ir jākoncentrē modelī. Nav nepieciešams pārvietot nevienu biznesa loģikas daļu kontrollerī vai skatā.

Mūsu modelī ir iekļauti divi skripti, no kuriem katram ir noteikta sava klase. Centrālā FriendList klase un Friend entītiju klase. Centrālajā klasē notiek datu manipulācijas: datu saņemšana no pārziņa un apstrāde. Entītijas klase kalpo kā konteiners datu pārsūtīšanai starp modeli un skatu, kā arī nosaka tās formātu. Labi ieviešot MVC modeli, kontrolierī nedrīkst būt atsauces uz entītiju klasēm, un tajās nedrīkst būt ietverta nekāda biznesa loģika. To mērķis ir tikai datu glabāšana.
Klasē FriendList, kas darbojas ar draugu sarakstu, mēs izveidojām funkciju, kas modelē šīs klases mijiedarbību ar datu bāzi. Metode getFriendList() atgriež objektu masīvu, kas izveidots no klases Draugs. Lai nodrošinātu ērtu darbu ar datiem, tika izveidota arī funkcija, kas indeksē objektu masīvu. Kontrolierim bija pieejamas tikai divas metodes: setKey() - iestata atslēgas lauku, ar kuru tiek atgriezti detalizēti dati par draugu; fetch() - atgriež vai nu konkrētu objektu, vai visu draugu sarakstu.

Saraksts Nr. 3 (modeļa fails FriendList.php):

Require_once($baseDir . "/lib/model/Friend.php"); klase FriendList ( privāts $oneKey; privāta funkcija getFriendList() ( return array(new Friend("Aleksandrs", "1985", " [aizsargāts ar e-pastu]"), jauns draugs ("Jurijs", "1987", " [aizsargāts ar e-pastu]"), jauns draugs ("Aleksejs", "1989", " [aizsargāts ar e-pastu]"),); ) privātā funkcija getIndexedList() ( $saraksts = array(); foreach($this->getFriendList() kā $val) ( $list[$val->getKey()] = $val; ) return $list ) publiskā funkcija setKey($key) ( $this->oneKey = $key; ) publiskā funkcija fetch() ( $aFriend = $this->getIndexedList(); return ($this->oneKey) ? $aFriend; [$this->oneKey] : $draugs;

Atkarībā no Entity objektu ieviešanas datus par to var formatēt kā XML dokumentu vai JSON objektu.

4. saraksts (entītijas fails Friend.php):

Klases draugs ( privāts $key; privāts $name; privāts $yearOfBirth; privāts $e-pasts; publiska funkcija __construct($name, $yearOfBirth, $email) ( $this->key = md5($name . $yearOfBirth . $email) $this->name = $this->yearOfBirth; >nosaukums ) publiskā funkcija getYearOfBirth() (atgriezt $this->yearOfBirth; ) publiskā funkcija getEmail() (atgriezt $this->email; ) )

Performance

Tagad mums ir jāsniedz dati lietotājam vislabākajā iespējamajā gaismā.

Ir pienācis laiks runāt par prezentāciju. Atkarībā no uzdevuma datus var nodot skatam dažādos formātos: vienkāršus objektus, XML dokumentus, JSON objektus utt. Mūsu gadījumā tiek nodots objekts vai objektu masīvs. Tajā pašā laikā mēs neuztraucāmies par bāzes slāņa izvadi - kas attiecas uz ģenerētās lapas kājeni un galveni, šis kods tiek atkārtots abos skata failos. Bet mūsu mazajam piemēram tas nav svarīgi.

Galvenais šeit ir parādīt, ka skats ir nošķirts no kontrollera un modeļa. Šajā gadījumā kontrolieris ir atbildīgs par datu pārsūtīšanu no modeļa uz skatu.

Mūsu piemērā skatā ir tikai divi faili: viens, lai parādītu detalizētu informāciju par draugu, un otrs, lai parādītu draugu sarakstu.

Saraksts Nr. 5 (fails draugu saraksta parādīšanai friendlist.php):

Mani draugi

Vārds Dzimšanas gads
getKey() ?>">getName() ?> getYearOfBirth() ?>


Saraksts Nr. 6 (fails draugu saraksta attēlošanai friendone.php):

<?php echo $oFriend->getName() ?> : Mans draugs getName() . "
"; echo "Dzimšanas gads: " . $oFriend->getYearOfBirth() . "
"; echo "E-pasts: " . $oFriend->getEmail() . "
"; ?> Saraksts

Ja visu šo kodu pārsūtīsiet uz tīmekļa serveri, tad rezultātā jūs iegūsit mikrovietni nevis no divām lapām (spriežot pēc prezentācijas failu skaita), bet gan ar četrām lapām. Pirmajā tiks parādīts draugu saraksts, bet pārējie trīs parādīs detalizētu informāciju par katru draugu.

Mēs varētu ieviest detalizētu pārlūkošanu, izmantojot AJAX, tad mums būtu tikai viena lapa, un mēs atveidotu daļu skata, izmantojot JSON objektus tieši klientu datoros. Šajā sakarā ir daudz iespēju.

Šis ir vienkāršots tīmekļa lietojumprogrammas piemērs, kura pamatā ir MVC modelis. Bet jau uz tā jūs varat redzēt daudz iespēju. Mēs jau esam iekļāvuši elastību un mērogojamību kā priekšrocības. Papildu priekšrocības būs iespēja standartizēt kodēšanu, kļūdu atklāšanas un labošanas vienkāršība, kā arī jaunu izstrādātāju ātra ienākšana projektā. Turklāt, izmantojot trešās puses tīmekļa pakalpojumus un mākoņdatu bāzes, varat mainīt veidu, kādā entītijas tiek saglabātas jūsu lietojumprogrammā. Vienīgais mīnuss ir neliels skriptu apjoma pieaugums. Un tā, nepārtrauktas priekšrocības. Tātad, izmantojiet to savai veselībai.

Šeit ir projekta faili, lejupielādējiet un salīdziniet:

Tā kā? Kādas domas? Komentēsim, nekautrējies.

Lai gan frāzes "vietnes arhitektūra" nozīme jums var būt intuitīva, mēģināsim to izpētīt vairākas definīcijas no autoritatīviem avotiem.

Arhitektūra- tā ir sistēmas pamatorganizācija, kas ietverta tās sastāvdaļās, to savstarpējās attiecībās un ar vidi, kā arī principiem, kas nosaka sistēmas izstrādi un attīstību.

Arhitektūra programma vai datorsistēma ir sistēmas struktūra vai struktūras, kas ietver programmas elementus, šo elementu ārēji redzamās īpašības un attiecības starp tiem [Bass].

Arhitektūra ir organizācijas struktūra un ar to saistītās sistēmas uzvedība. Arhitektu var rekursīvi sadalīt daļās, kas mijiedarbojas, izmantojot saskarnes, savienojumus, kas savieno detaļas, un detaļu montāžas nosacījumus. Daļas, kas mijiedarbojas, izmantojot saskarnes, ietver klases, komponentus un apakšsistēmas.

Arhitektūra Programmatūras sistēma vai sistēmu kopa sastāv no visiem svarīgajiem projektēšanas lēmumiem par programmas struktūrām un mijiedarbību starp šīm struktūrām, kas veido sistēmas. Dizaina lēmumi nodrošina vēlamo īpašību kopumu, kas sistēmai ir jāatbalsta, lai tā būtu veiksmīga. Dizaina risinājumi nodrošina konceptuālo sistēmu sistēmas izstrādei, atbalstam un uzturēšanai.

Tātad, kas ir programmu arhitektūra?

Iepriekšējā sadaļā sniegtās definīcijas ir pārāk sausas, lai tās uztvertu nesagatavots lasītājs. Mēs centīsimies pēc iespējas vienkāršāk izskaidrot būtību.

Kad programma kļūst pietiekami liela, programmētājs to sadala vairākos failos. Ja jūs nedomājat par līdzīgu funkciju grupu identificēšanu un ievietošanu atsevišķos moduļos, šāds sadalījums būs maz noderīgs. Kods nebūs atkārtoti lietojams, un tajā būs grūti orientēties. Programmu būs grūti paplašināt un mainīt.

Tādējādi rodas pirmais jautājums: kā sadalīt programmu failos. Programmas failu arhitektūra ir viens no tās struktūras aspektiem.

Identificējot sistēmas moduļus, ir jāsaprot to savstarpējā saistība. Piemēram, datu bāzes piekļuves un grafikas apstrādes moduļi, visticamāk, neko nezinās viens par otra esamību. Bet UI modulis zinās par tiem abiem (un viņi par to nezinās).

Sistēmas komponentu attiecības nosaka arī tās arhitektūra.

Kopumā visus būtiskos lēmumus, kas vērsti uz programmas organizēšanu, nevis ar tās palīdzību atrisinātajām problēmām, var attiecināt uz arhitektūru.

Abstrakcijas līmeņi

Veidojot moduļus (atlasot funkciju grupas un procesuālās pieejas gadījumā sadalot failos), ir svarīgi izcelt abstrakcijas un mēģināt tās sadalīt vairākos līmeņos.

Zema līmeņa moduļi ir pēc iespējas autonomi, tie nav atkarīgi no citām programmas daļām. Labi izstrādāti moduļi izolē “ārpasauli” no tiem uzticētās problēmas risināšanas sarežģītības. Izsaucēja puse zina tikai moduļa saskarni (ārējās funkcijas), iekšējā daļa tam ir slēgta.

Kā piemēru ņemsim fotogaleriju. Informācija par attēliem un lietotājiem tiek glabāta datu bāzē, lietotāja interfeiss ir sadalīts klienta daļā un admin panelī.

Programmas struktūra varētu būt līdzīga attēlā zemāk:


Šis piemērs izseko trīs abstrakcijas līmeņi.

Jebkurā programmā, kas paredzēta cilvēku lietošanai, ir vismaz divi līmeņi: lietotāja interfeiss un modelis. Lietotāja interfeisa modulis ir paredzēts datu prezentēšanai un vizualizācijai un lietotāju reakciju apstrādei. Modelis satur risināmās problēmas loģiku, un tas nekādā veidā nedrīkst būt atkarīgs no datu parādīšanas veida. Modelim jābūt viegli pārnēsājamam starp dažādiem lietotāja interfeisa veidiem.

MVC arhitektūra

Tagad populārs MVC dizaina modelis. Tas kalpo, lai atdalītu lietojumprogrammu loģiku no lietotāja interfeisa. Bet vispirms noskaidrosim, kas ir dizaina modelis.

Tas ir tipveida dizaina risinājumu kopums, arhitektūras karkass vai tā fragments. Ja bibliotēka ir atkārtoti lietojama koda pakotne, tad dizaina modelis ir atkārtoti lietojamu risinājumu pakete.

Ko MVC mums piedāvā, lai atdalītu lietojumprogrammu loģiku no lietotāja interfeisa?

Paraugs MVC ļauj atdalīties dati, prezentācija un lietotāja darbību apstrāde trīs atsevišķos komponentos:

  1. Modelis. Modelis nodrošina datus (parasti skatam) un arī atbild uz pieprasījumiem (parasti no Pārziņa), mainot tā stāvokli;
  2. Skatīt. Atbildīgs par informācijas attēlošanu (lietotāja interfeiss);
  3. Kontrolieris. Interpretē lietotāja ievadīto informāciju un informē modeli un skatu, lai tas atbilstoši reaģētu.

Zemāk esošajā attēlā parādītas attiecības starp ietvara komponentiem. Ilustrēsim attēlu ar nelielu piemēru.


Iedomājieties formu, kurā varat ievadīt tekstu, noklikšķiniet uz pogas Rediģēt un iegūstiet tās transliterāciju:


Atkārtosim diagrammā parādītās darbības:

  1. Lietotājs nospiež pogu Rediģēt un Performance(Skatīt) nosūta ziņojumu Uz kontrolieri(kontrolieris): "Komanda: rediģēt"
  2. Kontrolieris pieņem ziņojumu un piekļūst modelim, izsaucot Edit() metodi.
  3. Rezultātā modelis mainās tā stāvokli (tajā saglabāto transliterēto tekstu) un paziņo par to skatu: “Notikums: mainīts”.
  4. Iesniegums tiek pieņemts signālu un sazinās ar modeli, lai iegūtu jaunu rezultāta vērtību, izsaucot tā metodi Get().

MVC ieviešana

MVC ieviešana paredz uz objektu orientētu pieeju (OOP). Tomēr dizaina modelis ir tikai risinājumu kopums. Pielāgosim tos PHP, neizmantojot OOP. Vienkāršošana tiek veikta, lai koncentrētos uz loģikas atdalīšanas būtību, kā arī lai materiālu varētu izmantot lasītājs, kurš nepārzina OOP.

Paskatīsimies vēlreiz piemērs ar fotogaleriju.
Tam ir divi skatīšanās režīmi:

  1. Režīms sīktēlu skatīšanai (visi uzreiz);
  2. Režīms pilna izmēra fotoattēla (viena) skatīšanai.

Tas ir arī iespējams augšupielādēt fotoattēlus uz serveri. Turklāt mēs ieviesīsim atbalstu vizualizācijas veidiem, lai novērtētu ietvara elastību.

Tur būs divi ieejas punkti:

  1. index.php(galerijas skats);
  2. photo.php (skatīt pilna izmēra fotoattēlu).
Mēs izskatīsim šos divus failus Kontrolieri.

Modeļi būs modulis, kas nodrošina darbu ar attēlu krātuvi. Sauksim viņu galerija.php un ievietojiet to mapē modelis.

Lomā Pārstāvība Parādīsies HTML veidnes, tās atradīsies mapē veidnes. Kas ir veidnes un kam tās vajadzīgas, redzēsim vēlāk.

Galerijas skata un fotoattēlu skata lapām būs kopīga galvene un kājene, atšķirsies tikai centrālā daļa.

Galerijas skatam būs divu veidu vizualizācija:

  1. Kā tabula (noklusējums);
  2. Saraksta veidā.
Mums būs nepieciešamas četras veidnes:
  1. main.php(lapas ietvars);
  2. content_index_table.php (galerijas satura tabulas skats);
  3. content_index_list.php (galerijas satura saraksta skats);
  4. content_photo.php (fotoattēlu skatīšanas lapas saturs).

Tā rezultātā tiek izveidota šāda vietnes struktūra:


Faila struktūra ir sadalīta ar divām horizontālām joslām, veidojot trīs sadaļas. Augšējās sadaļas faili pieder modelim, vidējās sadaļas faili skatam un apakšējās sadaļas faili kontrolierim.

Modelis

Sāksim ar Modeļa ieviešanu. Tālāk norādītais kods nav parādīts pilnībā, lai samazinātu un labāku piemēru.

Mēs esam definējuši tikai modeļa saskarni, atstājot trūkstošo implementāciju. Tomēr, piemēram, MVC ietvara ieviešanai tas vispār nav vajadzīgs.

Performance

Tagad apskatīsim veidnes. Sāksim ar vispārīgo lapas rāmi:

<?=$title?>



Jūs nedrīkstat mulsināt fakts, ka veidnē tiek izmantoti mainīgie $title un $content, kas nāca no nekurienes. Kontrolieris tos iestatīs. Bet vairāk par to vēlāk.

- šī ir apzīmējuma saīsināta versija .

To ir ērti izmantot veidnēs. Ir arī ērtāk izmantot alternatīvas opcijas, lai veidnēs rakstītu if-else, foreach, for un while. Tie izskatās šādi:

Ja (<условие>): <тело>endifs; katram(<инициализация цикла>): <тело>endforeach;

Pārējās veidnes tiks ievietotas vietnē main.php šādi:

Tālāk esošie piemēri parāda to kodu:

Koda veidnes/content_index_table.php

Tabula| Saraksts

"> " />



Kodu veidnes/content_index_list.php

Tabula | Saraksts

"> " />



templates/content_photo.php: Atpakaļ

" />

Kontrolieris

Un visbeidzot, saliksim to visu kopā, aprakstot mūsu abus kontrolierus. Viņu uzdevums ir apstrādāt pieprasījumu, atlasīt veidni un aizstāt veidnei nepieciešamos datus. Dati parasti tiek ņemti no modeļa.

Galerijas kontrolieris ielādē fotoattēlu, ja lietotājs ir iesniedzis failu. Pretējā gadījumā tas no modeļa izvelk fotoattēlu sarakstu, atlasa vajadzīgo veidni (atkarībā no lietotāja vēlmes) un parāda šo veidni, nododot tai fotoattēlu sarakstu:

Kods index.php

Fotoattēlu skata kontrolleris ir vēl vienkāršāks:

Kods photo.php

Beidzot

Centieties pievērst pietiekami daudz uzmanības savas lietojumprogrammas arhitektūras izstrādei. Ja sākotnēji izveidojat spēcīgu un paplašināmu rāmi, pūles atmaksāsies simtkārtīgi.

Lai ieviestu MVC modeli, labāk izvēlēties uz objektu orientēta pieeja .

Ir daudz gatavu ietvara risinājumu, piemēram, Zend Framework. Tomēr šajā nodarbībā sniegtā informācija ir pietiekama, lai izprastu MVC arhitektūras risinājumus un sāktu tos izmantot jau tagad.

Dizaina raksts Modeļa skata kontrolieris (MVC) ir programmatūras arhitektūras modelis, kura pamatā ir datu attēlojuma nošķiršana no metodēm, kas mijiedarbojas ar datiem.

Lai gan MVC sākotnēji tika izstrādāts personālajiem datoriem, tīmekļa izstrādātāji to ir pielāgojuši un plaši izmantojuši, jo ir precīzi nošķirtas problēmas un iespēja atkārtoti izmantot kodu. Shēma veicina moduļu sistēmu izstrādi, kas ļauj izstrādātājiem ātri atjaunināt, pievienot vai noņemt funkcionalitāti.

Šajā rakstā aprakstīšu pamatprincipus, kā arī apskatīšu konstrukcijas shēmas definīciju un vienkāršu MVC PHP piemēru.

Kas ir MVC

Dizaina modeļa nosaukumu nosaka trīs galvenie komponenti: modelis, skats un kontrolieris. MVC modeļa vizuālais attēlojums izskatās kā parādīts attēlā diagrammu zemāk:

Attēlā parādīta vienvirziena datu plūsmas struktūra un tās ceļi starp dažādiem komponentiem, kā arī to mijiedarbība.

Modelis

Modelis ir pastāvīga datu krātuve, ko izmanto visā struktūrā. Tam ir jānodrošina piekļuve datiem apskatei, atlasei vai ierakstīšanai. Vispārējā struktūrā "Modelis" ir tilts starp "View" un "Controller" komponentiem.

Tomēr “Modelim” nav savienojuma vai informācijas par to, kas notiek ar datiem, kad tie tiek nodoti “View” vai “Controller” komponentiem. Vienīgais “Modeļa” uzdevums ir apstrādāt datus pastāvīgā glabāšanā, meklēt un sagatavot uz citām MVC sastāvdaļām pārsūtītos datus.

“Modelim” jādarbojas kā “vārtsargam”, stāvot netālu no datu noliktavas un neuzdodot jautājumus, bet pieņemot visus ienākošos pieprasījumus. Šī bieži vien ir vissarežģītākā MVC sistēmas daļa. Komponents “Modelis” ir visas struktūras augšdaļa, jo bez tā nav iespējams izveidot savienojumu starp “Controller” un “View”.

Performance

Skats ir sistēmas daļa, kurā no modeļa pieprasītajiem datiem tiek piešķirta galīgā izvades forma. Tīmekļa lietojumprogrammās, kas veidotas uz MVC, "Skats" ir komponents, kurā tiek ģenerēts un parādīts HTML kods.

Skats arī pārtver lietotāja darbību, kas pēc tam tiek nodota "kontrolierim". Tipisks piemērs tam ir poga, ko ģenerē "Skatīt". Kad lietotājs noklikšķina uz tā, tiek aktivizēta darbība “Controller”.

Pastāv vairāki izplatīti maldīgi priekšstati par skata komponentu. Piemēram, daudzi cilvēki kļūdaini uzskata, ka “Skats” nav saistīts ar “modeli”, un visi parādītie dati tiek pārsūtīti no “Controller”. Patiesībā šajā datu plūsmas diagrammā nav ņemta vērā MVC arhitektūras teorija. Savā rakstā Fabio Čevasko apraksta šo nepareizo pieeju, izmantojot piemēru par vienu no netradicionālajiem MVC PHP ietvariem:

"Lai pareizi lietotu MVC arhitektūru, nevajadzētu būt mijiedarbībai starp modeli un skatu: visu loģiku apstrādā kontrolieris."

Turklāt neprecīza ir arī “View” kā veidnes faila definīcija. Bet tā nav vienas personas vaina, bet gan dažādu izstrādātāju daudzu kļūdu rezultāts, kas izraisa vispārēju nepareizu priekšstatu. Tad viņi to nepareizi izskaidro citiem. Faktiski "View" ir daudz vairāk nekā tikai veidne. Taču mūsdienu uz MVC orientētie ietvari ir absorbējuši šo pieeju tiktāl, ka nevienam vairs nav vienalga, vai tiek atbalstīta pareizā MVC struktūra.

Komponents View nekad nenodod datus tieši no kontroliera. Nav tieša savienojuma starp "View" un "Controller" - tie ir savienoti, izmantojot "Model".

Kontrolieris

Tās uzdevums ir apstrādāt lietotāja ievadītos datus un atjaunināt “Modeli”. Šī ir vienīgā diagrammas daļa, kurai nepieciešama lietotāja iejaukšanās.

“Kontroleri” var definēt kā informācijas savācēju, kas pēc tam tiek pārsūtīta uz “Modeli” un pēc tam organizēta uzglabāšanai. Tajā nav citas loģikas, izņemot vajadzību vākt ienākošos datus. "Controller" arī savieno tikai vienu "View" un vienu "modeli". Tādējādi tiek izveidota vienvirziena datu plūsmas sistēma ar vienu ieeju un vienu izvadi sakaru punktos.

Kontrolieris saņem izpildāmos uzdevumus tikai tad, kad lietotājs mijiedarbojas ar skatu, un katra funkcija ir atkarīga no lietotāja mijiedarbības ar skatu. Izstrādātāju visbiežāk pieļaujamā kļūda ir tā, ka viņi jauc kontrolieri ar vārteju, tāpēc piešķir tam funkcijas un uzdevumus, kas pieder skatam.

Vēl viena izplatīta kļūda ir nodrošināt “Controller” funkcijas, kas ir atbildīgas tikai par datu apstrādi un pārsūtīšanu no “Modeļa” uz “View”. Bet saskaņā ar MVC modeļa struktūru šī mijiedarbība jāveic starp “modeli” un “skatu”.

MVC PHP

Rakstīsim tīmekļa aplikāciju PHP valodā, kuras arhitektūra ir balstīta uz MVC. Sāksim ar stiepļu rāmja piemēru:

string = "MVC + PHP = Lieliski!"; ) )kontrolleris = $kontrolleris; $šis->modelis = $modelis; ) publiskās funkcijas izvade() ( return "

" . $this->model->string ."

"; } } modelis = $modelis; ) )

Mums ir projekts ar vairākām galvenajām klasēm katrai veidnes daļai. Tagad jums ir jākonfigurē attiecības starp tām:

izvade();

Iepriekš minētajā PHP MVC piemērā kontrolierim nav noteiktas funkcionalitātes, jo lietojumprogramma nedefinē lietotāja mijiedarbību. Skatā ir visas funkcionalitātes, jo mūsu piemērs ir paredzēts tikai demonstrācijas nolūkiem.

Izvērsīsim piemēru, lai parādītu, kā mēs pievienosim kontroliera funkcionalitāti, tādējādi pievienojot lietojumprogrammai mijiedarbību:

string = "MVC + PHP = Lieliski, noklikšķiniet šeit!"; ) )kontrolleris = $kontrolleris; $šis->modelis = $modelis; ) publiskās funkcijas izvade() ( return "

modelis->virkne. "

"; } } modelis = $modelis; ) public function clicked() ( $this->model->string = "Atjaunināti dati, pateicoties MVC un PHP!"; ) )

Mēs esam paplašinājuši programmu ar pamata funkcionalitāti. Komponentu mijiedarbības iestatīšana tagad izskatās šādi:

($_GET["darbība"])(); ) echo $view->output();




Tops