Pagbuo ng isang application ayon sa template Disenyo ng MVC(model-view-controller) ay tipikal para sa Java at, kapag inilapat sa DroidScript, tila hindi maintindihan at hindi kailangan. Bakit gawing kumplikado ang lahat? Nakakuha ang MVC ng aura ng pagiging kumplikado at "magic" dahil sa paggamit ng maganda ngunit hindi maintindihan na mga salita (konsepto, modelo, lohika ng negosyo, pattern) at mga kumplikadong demonstrasyon sa konteksto ng Java kapag isinasaalang-alang ito. Ang lahat ay mas simple: Ang MVC ay isa sa mga pattern ng disenyo na gumagawa karagdagangpagbabahagi ng code sa object-oriented kapaligiran.
Ang pangunahing elemento ng MVC model ay ang controller - normal na aplikasyon DroidScript, kung saan nauugnay ang code sa visual markup at panlabas na disenyo mga widget, pati na rin ang data at mga pamamaraan para sa pag-access sa mga ito. Sa pamamagitan ng data, nakasanayan na naming maunawaan ang impormasyong nakaimbak sa mga array, file, at database. Ngunit sa konsepto ng MVC, ang data ay naiintindihan sa sa malawak na kahulugan Ang mga salita ay lahat ng bagay na hindi code ng aplikasyon:
- panlabas na data mula sa mga file at database - metadata, text, graphics, tunog, musika, atbp.
- data ng panloob na application - mga linya na may mga label sa mga pindutan at iba pang mga kontrol, text in mga dialog box, paglalarawan ng mga istilo, constants, software na nabuong graphics, atbp.
Mula sa pananaw ng gumagamit, ang kanyang karanasan sa application ay hindi nagbabago kapag gumagamit ng MVC: nag-click din siya sa mga pindutan, pinipili ang data at kung paano ito ipinapakita. Maaaring alalahanin ang mga pagbabago pasilidad gawaing ito. At sa bahagi ng pag-unlad, ang mga pagbabago ay kapansin-pansin: ang pakikipag-ugnayan sa pagitan ng data at pagpapakita nito sa konsepto ng MVC ay nangyayari sa pamamagitan ng controller at sa ilalim ng kontrol nito.
Tingnan muna natin ang isang simpleng halimbawa ng paggamit ng MVC sa isang single-file na application.
Single-file na pagpapatupad ng MVC model
Kumuha tayo ng isang simpleng aplikasyon.
Function OnStart())( var _lay = app.CreateLayout("linear", "VCenter,FillXY"); var _btnShowVersion = app.CreateButton("Show version", 0.3, 0.1); _btnShowVersion.SetBackColor("9#66777") ; _btnShowVersion.SetMargins(0, 0.05, 0, 0);
Sa unang tingin, ang lahat ay tila maganda, ngunit ipagpalagay na kailangan mong magbago scheme ng kulay application at display text sa ilang wika. Ito ay hahantong sa mga komplikasyon, dahil ang lahat ng data sa halimbawang ipinakita ay mga nakapirming halaga (literal). Ito ay makabuluhang binabawasan ang flexibility ng code at kumplikado ang pag-debug at suporta nito.
Ang isa pang disbentaha ay ang data - ang mga label sa pindutan, ang markup - mga pamamaraan para sa pagpapakita ng mga widget, at ang aksyon - ang bloke ng code na nagbabago sa label sa pindutan kapag ito ay na-click ay matatagpuan sa isang bloke at sa isang file. Iyon ay, upang baguhin ang label na kailangan mong buksan ang file na ito at makakuha ng access sa buong code ng application. Ito ay tulad ng pagpapalit ng gulong ng kotse na nangangailangan ng pag-disassemble sa katawan ng kotse upang makakuha ng access sa lahat ng nilalaman. Para saan? Sa panahon ng proseso ng pag-disassembling sa katawan ng kotse, maaari mong aksidenteng mahuli ang isang bagay at gawin itong hindi gumagana. Posible rin sa code: Nais kong palitan ang pangalan ng isang linya sa isang lugar, ngunit ang pagpapalit ay naganap sa buong file, na humantong sa isang pagkakalat ng mga error. O gusto mo lang baguhin ang kulay ng button, ngunit hindi sinasadyang nahuli ang code sa malapit at ang buong application ay tumigil sa paggana.
Ang isa sa mga gawain ng pattern ng MVC ay tiyak na pag-iba-iba ang pag-access: una, ang module (o bloke ng code) na pinagmumulan ng error ay natukoy, at pagkatapos ay ang pag-access ay ibinibigay lamang dito. Bakit magbibigay ng access sa electronics at makina ng isang kotse kung kailangan mong magpalit ng gulong?
Kung ang pag-unlad ay isinasagawa sa isang file, madalas itong nangyayari tulad nito: ang mga bagong pag-andar ay inilalagay sa lugar, sa pinakadulo simula o dulo ng code, na sa paglipas ng panahon ay humahantong sa kanilang paghahalo. Idagdag natin dito ang paghahalo ng code sa mga function mismo, at sa isang buwan kahit na may mga komento ay mahirap maunawaan ang lahat ng ito.
Ipatupad natin ang halimbawang ipinakita sa itaas sa konteksto ng MVC. Upang gawin ito, ang lahat ng code ay kailangang hatiin at ipangkat sa naaangkop na mga bloke. Ang pagkakasunud-sunod ng mga bloke sa code ay hindi mahalaga, ngunit ito ay mas mahusay na sumunod sa lohika: para sa controller upang gumana, ang parehong data at mga elemento para sa pagpapakita ng mga ito ay kinakailangan, kaya ito ay ilagay sa huli. Kapag ipinakita ang data, dapat itong umiiral. Nangangahulugan ito na nauuna ang bloke ng modelo:
- Modelo
- Pagganap
- Controller
//+++ pampublikong pamamaraan para sa pag-access ng data _obj.getVersion = function())( return _version; ) _obj.btnGetTitle = function())( return _titleShowVersion; ) // --- bukas pamamaraan para sa pag-access ng data window.model = _obj; // buksan ang access sa lokal na bagay ))(); //--- model //+++ presentation (function ()( var _lay = app.CreateLayout("linear", "VCenter,FillXY"); var _btnShowVersion = app.CreateButton(window.model.btnGetTitle(), 0.3, 0.1); _btnShowVersion.name = "_btnShowVersion";
))(); //--- presentation //+++ controller (function(p_object)( var _obj = ; // public object search method _obj.findObjectById = function(p_name)( var _objectList = app.GetObjects(); para sa (var _i sa _objectList)( if(_objectList[_i].name == p_name)( return _objectList[ _i]; ) ) return null.control = _obj))(); function na OnStart())( var _buttonShowVersion = window.control.findObjectById("_btnShowVersion"); //+++ action _buttonShowVersion.SetOnTouch(function())( this.SetText(window.model.getVersion()); )) ; / / --- aksyon ) //--- controller
Dahil sa paghihiwalay ng mga function, ang application code ay tumaas ng maraming beses.
Sa una, ang lahat ng mga variable ay ginawang pribado at sa dulo lamang, kung kinakailangan, ang pag-access sa mga ito ay mabubuksan sa pamamagitan ng global bagay sa bintana, na nagbibigay-daan sa iyong gawin nang walang mga global na variable.
Ang halimbawa ay nagpapatupad ng paghahanap ng widget, tulad ng sa Java, ngunit magagawa mo itong mas simple at gawing mas mahusay ang code sa pamamagitan ng pagbubukas ng access sa object sa pamamagitan ng global associative array:
Window.controls = ;
window.controls.buttonShowVersion = _btnShowVersion;
Ang data, ang pagpapakita nito at ang reaksyon sa mga aksyon ay matatagpuan sa iba't ibang mga bloke, nang walang paghahalo sa isa't isa, na nagpapahintulot sa developer na magtrabaho sa kanila nang mas madali. Kung mas madaling magtrabaho kasama ang data at code, mas kaunting mga error ang magkakaroon, mas madali itong i-debug, suportahan at sukatin.
Hindi kinakailangang paghiwalayin ang lahat ng tatlong sangkap na ito sa isa't isa. Mayroong ilang mga variation ng MVC, pati na rin ang mga hindi kumpletong pagpapatupad ng modelong ito. Halimbawa, maaari mong paghiwalayin ang data at pagsamahin ang code ng pagkilos sa mga kontrol gamit ang mga function ng anonymous na callback.
Kapag nagtatrabaho sa isang object-oriented na kapaligiran, ang paghihiwalay ng code at data ay naroroon na sa simula: ang data at mga aksyon ay pinagsama-sama sa mga klase, ang mga bagay ay nakikipag-ugnayan sa isa't isa sa pamamagitan ng bukas na mga pamamaraan atbp. Ang MVC ay nagbibigay-daan para sa isang mas pino, mas tahasang paghihiwalay ng code at data sa kanilang mga pangunahing function.
Upang mas maunawaan ang mga benepisyo ng paggamit ng modelong MVC, tingnan natin ang paghahati ng code sa magkakahiwalay na mga file.
Tatlong file na pagpapatupad ng MVC model
Code division sa pamamagitan ng iba't ibang mga file ginagamit para sa higit pa komportableng trabaho kasama niya. Ang malaking bilang ng maliliit na file na makikita sa mga proyekto ng MVC ay maaaring magtanong sa pahayag na ito, ngunit ang pagkakita sa mga file ay isang bagay, ang pagtatrabaho sa kanila ay isa pa. Sa bawat sandali ng oras, nakikipag-ugnayan ang developer sa isang file mula sa ilang maliit na hanay ng mga ito. Upang gawin ito, kailangan mong magkaroon ng isang mahusay na pag-unawa sa istraktura ng organisasyon ng proyekto at patuloy na subaybayan tatlong file- modelo, view at controller, upang hindi aksidenteng ma-edit ang code ng third-party. Dahil sa mga limitasyon ng editor ng DroidScript, ang ganitong pagpapangkat ay posible lamang sa pamamagitan ng mga pangalan ng file sa root directory, halimbawa:
myproject_model.js - modelo
myproject_view.js - tingnan
myproject_control.js - controller
Nasa ibaba ang isang halimbawa ng paghahati ng code ng nakaraang halimbawa sa mga file.
myproject_model.js - modelo(function())( var _obj = ; //+++ data var _version = "Bersyon ng application 1.0"; //--- data //+++ string resource var _titleShowVersion = "Ipakita ang bersyon"; //++ + string resource _obj.getVersion = function() ( return _version; ) _obj.btnGetTitle = function() ( return _titleShowVersion; ) window.model = _obj ))(); myproject_view.js - tingnan(function ()( var _lay = app.CreateLayout("linear", "VCenter,FillXY"); var _btnShowVersion = app.CreateButton(window.model.btnGetTitle(), 0.3, 0.1); _btnShowVersion.name = "_btnShowVersion.name ; _btnShowVersion.SetBackColor("#66778976"); myproject_control.js - controller app.LoadScript("myproject_model.js"); app.LoadScript("myproject_view.js");(function(p_object)( var _obj = ; // paraan ng paghahanap ng object _obj.findObjectById = function(p_name)( var _objectList = app.GetObjects(); para sa (var _i sa _objectList)( if(_objectList[_i].name = = p_name)( return _objectList[ _i]; ) ) return null.control = _obj; function na OnStart())( var _buttonShowVersion = window.control.findObjectById("_btnShowVersion"); //+++ action _buttonShowVersion.SetOnTouch(function())( this.SetText(window.model.getVersion()); )) ; // --- aksyon )Ang simpleng paghahati ng code sa mga file ay hindi madali. Upang gawin ito, ang isang koneksyon ay naitatag nang maaga sa modelo sa pamamagitan ng pampublikong ari-arian global root object - bintana.modelo, at ang koneksyon sa representasyon ay sa pamamagitan ng pandaigdigang hanay _mapa sa pamamagitan ng pamamaraan app.GetObjects.
Ang bentahe ng paghahati ng code sa mga file ay maaari mo na ngayong palitan ang code bilang isang buong bloke, halimbawa, para sa mabilis na paglulunsad ipatupad ang proyekto simpleng modelo, at pagkatapos ay palitan ang file ng isa pang mas functional, ngunit may parehong pangalan at interface. Ang diskarte na ito ay ginagamit, halimbawa, kapag nag-aayos ng kagamitan. Kung ang mga naunang pag-aayos ay binubuo ng isang mabagal at maingat na paghahanap at pagpapalit ng mga nabigong bahagi ng radyo, ngayon ay pinapalitan ang mga karaniwang unit. Ang halaga ng pag-aayos ng isang kumplikadong board ay makabuluhang mas mataas kaysa sa mabilis na pagpapalit nito.
Ito ay sumusunod mula sa itaas na ang isang mahusay na dinisenyo na interface ay maaaring makabuluhang pasimplehin ang kasunod na pagsasama ng mga module.
SA Mga bagay ng JavaScript ay ipinasa sa pamamagitan ng sanggunian. Ang pagpapalit ng mga katangian ng widget sa controller ay magbabago sa mga katangian kanyang sarili widget. Sa teorya, posibleng paghiwalayin ang mga view na object mula sa mga code object, tulad ng ginagawa sa Java, kung saan ginagamit ang mga xml structure bilang una, ngunit may maliit na punto dito sa dalawang dahilan - kawalan sa DroidScript visual editor interface at isang limitadong hanay ng mga available na katangian ng mga object ng API.
Multi-file na pagpapatupad ng MVC model
Depende sa mga gawaing itinalaga at sa pagiging kumplikado ng proyekto, ang mga detalye ng paghihiwalay ng code at data, sa pangkalahatan, ay maaaring magkakaiba. Maaari mong higit pang paghiwalayin ang data ng user mula sa mga mapagkukunan, maaari kang mag-drill down sa mga mapagkukunan ayon sa uri, mga aksyon ng grupo, atbp. Ngunit hindi ka pinapayagan ng editor ng DroidScript na gumana nang ganap gamit ang MVC.
Ang pattern ng Model-View-Controller (MVC) ay lubhang kapaki-pakinabang kapag lumilikha ng mga application na may kumplikado graphical na interface o pag-uugali. Ngunit para din sa higit pa mga simpleng kaso magkakasya din ito. Sa post na ito gagawa kami ng isang larong minesweeper na idinisenyo batay sa pattern na ito. Pinili ang Python bilang wika ng pag-unlad, ngunit hindi ito partikular na mahalaga. Ang mga pattern ay hindi nakadepende sa isang partikular na programming language at madali mong mailipat ang resultang pagpapatupad sa anumang iba pang platform.
Advertising
Maikling tungkol sa pattern ng MVC
Gaya ng ipinahihiwatig ng pangalan, ang MVC pattern ay may kasamang 3 bahagi: Modelo, View at Controller. Ang bawat isa sa mga bahagi ay gumaganap ng papel nito at maaaring palitan. Nangangahulugan ito na ang mga bahagi ay konektado sa isa't isa lamang sa pamamagitan ng ilang malinaw na mga interface, kung saan ang anumang pagpapatupad ay maaaring magsinungaling. Ang diskarte na ito ay nagpapahintulot sa iyo na palitan at pagsamahin iba't ibang sangkap, na nagbibigay ng kinakailangang lohika sa pagpapatakbo o hitsura mga aplikasyon. Tingnan natin ang mga pag-andar na ginagawa ng bawat bahagi.
Modelo
Responsable para sa panloob na lohika ng programa. Dito maaari nating itago ang mga paraan ng pag-iimbak ng data, pati na rin ang mga patakaran at algorithm para sa pagproseso ng impormasyon.
Halimbawa, para sa isang application maaari kaming lumikha ng ilang mga modelo. Ang isa ay magiging debug at ang isa ay gagana. Ang una ay maaaring mag-imbak ng data nito sa memorya o sa isang file, at ang pangalawa ay gumagamit na ng database. Sa esensya, isa lang itong Strategy pattern.
Pagganap
Responsable sa pagpapakita ng data ng Modelo. Sa antas na ito, nagbibigay lang kami ng interface para sa pakikipag-ugnayan ng user sa Modelo. Ang punto ng pagpapakilala ng bahaging ito ay kapareho ng sa kaso ng pagbibigay ng iba't ibang paraan ng pag-iimbak ng data batay sa ilang Modelo.
Halimbawa, sa mga unang yugto ng pag-unlad, maaari kaming lumikha ng isang simpleng view ng console para sa aming aplikasyon, at pagkatapos lamang magdagdag ng isang GUI na maganda ang disenyo. Bukod dito, nananatiling posible na i-save ang parehong uri ng mga interface.
Bilang karagdagan, dapat itong isaalang-alang na ang mga responsibilidad ng View ay kasama lamang ang napapanahong pagpapakita ng estado ng Modelo. Ang Controller ay responsable para sa pagproseso ng mga aksyon ng user, na pag-uusapan natin ngayon.
Controller
Nagbibigay ng link sa pagitan ng Modelo at ng mga pagkilos ng user na nagreresulta mula sa pakikipag-ugnayan sa View. Inuugnay ang mga sandali ng pag-update ng mga estado ng Modelo at View. Gumagawa ng karamihan sa mga pagpapasya tungkol sa mga paglipat ng application mula sa isang estado patungo sa isa pa.
Sa katunayan, para sa bawat pagkilos na maaaring gawin ng isang user sa isang View, dapat tukuyin ang isang handler sa Controller. Gagawin ng handler na ito ang mga naaangkop na manipulasyon sa modelo at, kung kinakailangan, aabisuhan ang View na may mga pagbabago.
Advertising
Mga pagtutukoy ng laro ng Minesweeper
Sapat na teorya. Ngayon ay magpatuloy tayo sa pagsasanay. Para sa demonstrasyon MVC pattern susulat tayo ng isang simpleng laro: Minesweeper. Ang mga patakaran ng laro ay medyo simple:
- Ang playing field ay isang hugis-parihaba na lugar na binubuo ng mga cell. Ang ilang mga cell ay may mga mina na random na inilagay sa mga ito, ngunit hindi alam ng manlalaro ang mga ito;
- Maaaring mag-click ang manlalaro sa anumang cell ng playing field gamit ang kaliwa o kanang mga pindutan daga;
- Ang pag-click sa kaliwang pindutan ng mouse ay nagiging sanhi ng pagbukas ng cell. Bukod dito, kung mayroong isang minahan sa cell, pagkatapos ang laro ay nagtatapos sa isang pagkawala. Kung may mga mina sa katabing mga cell sa tabi ng isang bukas, pagkatapos ay isang counter na may bilang ng mga mina sa paligid ay ipapakita sa bukas na cell. Kung walang mga minahan sa paligid ng bukas na cell, ang bawat kalapit na cell ay magbubukas ayon sa parehong prinsipyo. Ibig sabihin, magbubukas ang mga cell hanggang sa maabot nila ang hangganan ng playing field, o maabot ang mga bukas na cell, o walang minahan sa tabi nila;
- Ang pag-click sa kanang pindutan ng mouse ay nagpapahintulot sa iyo na gumawa ng mga marka sa mga cell. Ang pag-click sa isang saradong cell ay minarkahan ito ng isang bandila, na nagla-lock sa estado nito at pinipigilan ang hindi sinasadyang pagbubukas. Ang pag-click sa isang kahon na may markang bandila ay nagbabago ng marka nito sa isang tandang pananong. Sa kasong ito, ang cell ay hindi na naka-block at maaaring mabuksan gamit ang kaliwang pindutan ng mouse. Mag-click sa cell na may tandang pananong ibinabalik ito sa isang saradong estado na walang marka;
- Ang tagumpay ay tinutukoy ng estado ng laro kung saan ang lahat ng mga cell sa larangan ng paglalaro ay bukas, maliban sa mga may mina.
Ang isang halimbawa ng kung ano ang makukuha natin ay ibinigay sa ibaba:
UML diagram ng laro Minesweeper
Bago magpatuloy sa pagsulat ng code, magandang ideya na pag-isipan nang maaga ang arkitektura ng application. Hindi ito dapat nakadepende sa wika ng pagpapatupad, kaya ang UML ay pinakamainam para sa aming mga layunin.
Game Cell State Diagram
Anumang cell sa playing field ay maaaring nasa isa sa 4 na estado:
- Ang hawla ay sarado;
- Bukas ang hawla;
- Ang cell ay minarkahan ng isang bandila;
- Ang cell ay minarkahan ng tandang pananong.
Dito ay tinukoy lamang natin ang mga estado na makabuluhan para sa Representasyon. Dahil ang mga mina ay hindi ipinapakita sa panahon ng laro, kung gayon pangunahing hanay hindi ibinigay ang kaukulang estado. Tukuyin natin ang mga posibleng paglipat mula sa isang estado ng cell patungo sa isa pang gamit Mga Diagram ng UML Kundisyon:
Minesweeper Game Class Diagram
Dahil nagpasya kaming buuin ang aming application batay sa pattern ng MVC, magkakaroon kami ng tatlong pangunahing klase: MinesweeperModel, MinesweeperView at MinesweeperController, pati na rin ang isang helper class na MinesweeperCell upang iimbak ang estado ng cell. Tingnan natin ang kanilang class diagram:
Ang organisasyon ng arkitektura ay medyo simple. Dito ay ipinamahagi lang namin ang mga gawain sa bawat klase ayon sa mga prinsipyo ng pattern ng MVC:
- Sa pinakailalim ng hierarchy ay ang game cell class na MinesweeperCell. Iniimbak nito ang posisyon ng cell, na tinutukoy ng hilera ng hilera at haligi ng hanay ng larangan ng paglalaro; isa sa mga estado ang nagsasaad na inilarawan namin sa nakaraang subsection; impormasyon tungkol sa pagkakaroon ng isang minahan sa isang cell (mined) at isang counter ng mga minahan sa mga kalapit na cell counter. Bilang karagdagan, mayroon itong dalawang pamamaraan: nextMark() para sa pagbibisikleta sa mga estadong nauugnay sa mga marka na lalabas bilang resulta ng isang right-click, at open() na humahawak sa left-click na kaganapan;
- Sa itaas lang ay ang klase ng Modelo ng MinesweeperModel. Siya ang lalagyan para sa mga cell ng laro ng MinesweeperCell. Ang unang paraan nito, ang startGame(), ay naghahanda ng playing field para magsimula ang laro. Sinusuri ng isWin() method ang field ng laro para sa isang win state at nagbabalik ng true kung mananalo ang player, kung hindi, ito ay magbabalik ng false. Ang isang katulad na paraan ayGameOver() ay ginagamit upang suriin ang pagkawala. Ang mga pamamaraan ng openCell() at nextCellMark() ay nagtalaga lamang ng mga aksyon sa kaukulang mga cell sa playing field, at ibinabalik ng getCell() method ang hiniling na playing cell;
- Kasama sa klase ng MinesweeperView sumusunod na pamamaraan: syncWithModel() - tinitiyak ang pag-redrawing ng View upang ipakita ang kasalukuyang estado ng playing field sa Model; getGameSettings() - nagbabalik ng mga setting ng laro, tinukoy ng gumagamit; createBoard() - lumilikha ng playing field batay sa data ng Modelo; showWinMessage() at showGameOverMessage() ay nagpapakita ng mga mensahe ng panalo at pagkatalo;
- At panghuli ang klase ng Controller na MinesweeperController. Tinutukoy lamang nito ang tatlong pamamaraan para sa bawat isa posibleng aksyon player: startNewGame() ay responsable para sa pagpindot sa " button Bagong laro" sa interface ng View; pinangangasiwaan ng onLeftClick() at onRightClick() ang mga pag-click sa mga cell ng laro gamit ang kaliwa at kanang mga pindutan ng mouse, ayon sa pagkakabanggit.
Pagpapatupad ng larong Minesweeper sa Python
Panahon na upang simulan ang pagpapatupad ng aming proyekto. Piliin natin ang Python bilang development language. Pagkatapos ay isusulat natin ang klase ng View batay sa module ng tkinter.
Ngunit magsimula tayo sa Modelo.
Modelong MinsweeperModel
Pagpapatupad ng modelo sa Wikang sawa ganito ang hitsura:
MIN_ROW_COUNT = 5 MAX_ROW_COUNT = 30 MIN_COLUMN_COUNT = 5 MAX_COLUMN_COUNT = 30 MIN_MINE_COUNT = 1 MAX_MINE_COUNT = 800 class na MinesweeperCell: # Mga posibleng estado game cell: # closed - closed # open - open # flagged - minarkahan ng flag # questioned - minarkahan ng tandang pananong def __init__(self, row, column): self.row = row self.column = column self.state = "closed" self .mined = False self.counter = 0 markSequence = [ "closed", "flagged", "questioned" ] def nextMark(self): kung self.state sa 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 = "open" class MinesweeperModel: def __init__ (self): self.startGame() def startGame(self, rowCount = 15, columnCount = 15, mineCount = 15): kung rowCount in range(MIN_ROW_COUNT, MAX_ROW_COUNT + 1): unt self.rowCount = rowCount kung columnCo nasa saklaw(MIN_COLUMN_COUNT , MAX_COLUMN_COUNT + 1): self.columnCount = columnCount kung 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)
Sa itaas, tinutukoy namin ang hanay ng mga katanggap-tanggap na setting ng laro:
MIN_ROW_COUNT = 5 MAX_ROW_COUNT = 30 MIN_COLUMN_COUNT = 5 MAX_COLUMN_COUNT = 30 MIN_MINE_COUNT = 1 MAX_MINE_COUNT = 800
Sa pangkalahatan, ang mga setting na ito ay maaari ding gawing bahagi ng Modelo. Gayunpaman, ang laki ng field at ang bilang ng mga mina ay medyo static na impormasyon at malamang na hindi magbago nang madalas.
Susunod na tinukoy namin ang klase ng cell ng laro na MinesweeperCell. Ito ay naging medyo simple. Sa tagabuo ng klase, ang mga patlang ng cell ay sinisimulan na may mga default na halaga. Susunod, upang pasimplehin ang pagpapatupad ng cyclic state transition, ginagamit namin ang markSequence auxiliary list. Kung ang cell ay nasa "bukas" na estado, na hindi kasama sa listahang ito, walang mangyayari sa nextMark() na pamamaraan, kung hindi man ang cell ay mapupunta sa susunod na estado, at mula sa huling "tinanong" na estado ito ay "tumalon " sa paunang "sarado" na estado ". Sa open() na pamamaraan, sinusuri namin ang estado ng cell, at kung hindi ito "na-flag", pagkatapos ay ang cell ay napupunta sa bukas na estado na "nabuksan".
Susunod ay ang kahulugan ng klase ng MinesweeperModel Model. Ang paraan ng startGame() ay nagla-layout ng playing field gamit ang rowCount , columnCount at mineCount na mga parameter na ipinasa dito. Ang bawat parameter ay sinusuri upang makita kung ito ay nasa loob ng katanggap-tanggap na hanay ng mga halaga. Kung ang ipinadalang halaga ay nasa labas ng saklaw, ang halaga ng parameter ng playing field ay nai-save at hindi nagbabago. Dapat tandaan na mayroong karagdagang tseke para sa bilang ng mga minahan. Kung ang inilipat na bilang ng mga mina ay lumampas sa laki ng field, pagkatapos ay nililimitahan namin ito sa bilang ng mga cell na walang yunit. Bagaman, siyempre, ang naturang laro ay hindi gaanong makatuwiran at makukumpleto sa isang hakbang, upang makabuo ka ng ilan sa iyong sariling mga patakaran para sa naturang kaso.
Ang playing field ay nakaimbak bilang isang listahan ng mga listahan ng mga cell sa variable ng cellsTable. Bukod dito, pakitandaan na sa paraan ng startGame(), tanging ang halaga ng posisyon ng mga cell ang nakatakda, ngunit ang mga mina ay hindi pa nailalagay. Ngunit ang variable na firstStep ay tinukoy sa halagang True . Ito ay kinakailangan upang maalis ang elemento ng pagkakataon mula sa unang hakbang at maiwasan ang agarang pagkawala. Ang mga mina ay ilalagay pagkatapos ng unang paglipat sa natitirang mga cell.
Ang getCell() method ay nagbabalik lamang sa cell ng playing field ayon sa row row at column column. Kung ang halaga ng row o column ay hindi wasto, pagkatapos ay Wala ang ibinalik.
Ang isWin() method ay nagbabalik ng True kung ang lahat ng natitirang hindi nabuksan na mga cell ng playing field ay mina, iyon ay, sa kaso ng tagumpay, kung hindi, ito ay magbabalik ng False. Ang isGameOver() method ay nagbabalik lamang ng value ng gameOver class attribute.
Ang openCell() method ay nagde-delegate ng open() na tawag sa game cell object, na matatagpuan sa playing field sa posisyong tinukoy sa mga parameter ng method. Kung ang bukas na cell ay lumabas na mina, pagkatapos ay itinakda namin ang halaga ng gameOver sa True at lumabas sa pamamaraan. Kung hindi pa tapos ang laro, titingnan namin kung ito ang unang hakbang sa pamamagitan ng pagsuri sa halaga ng firstStep . Kung ito talaga ang unang hakbang, ang mga mina ay ilalagay sa playing field gamit ang generateMines() auxiliary method, na pag-uusapan natin sa ibang pagkakataon. Susunod, binibilang namin ang bilang ng mga mineng kalapit na cell at itinakda ang katumbas na halaga ng counter attribute para sa cell na pinoproseso. Kung ang counter ay zero, pagkatapos ay humihiling kami ng isang listahan ng mga kalapit na cell gamit ang getCellNeighbours() na pamamaraan at pabalik-balik na tawagan ang openCell() na pamamaraan para sa lahat ng saradong "kapitbahay", iyon ay, para sa mga cell na may "sarado" na katayuan.
Ang nextCellMark() method ay nagdedelegate lang ng tawag sa nextMark() method para sa cell na matatagpuan sa naipasa na posisyon.
Ang paglalagay ng mga mina ay nangyayari sa paraan ng generateMines(). Dito kami ay random na pumili ng isang posisyon sa playing field at suriin na ang cell sa posisyon na ito ay hindi bukas at hindi pa namimina. Kung matugunan ang parehong kundisyon, itatakda namin ang halaga ng mina na katangian sa True, kung hindi, patuloy kaming naghahanap ng isa pang libreng cell. Huwag kalimutan na upang magamit ang random na module sa Python, kailangan mong tahasan itong i-import gamit ang pag-import ng random na command.
Ang countMinesAroundCell() na paraan para sa pagbibilang ng bilang ng mga mina sa paligid ng isang partikular na cell ng playing field ay ganap na nakabatay sa getCellNeighbours() na pamamaraan. Ang paghiling ng "kapitbahay" ng isang cell sa pamamaraang getCellNeighbours() ay ipinapatupad din nang napakasimple. Sa tingin ko hindi ka magkakaroon ng anumang problema dito.
MinesweeperView
Ngayon pumunta tayo sa palabas. Ang code ng Python para sa klase ng MinesweeperView ay nasa ibaba:
Class MinesweeperView(Frame): def __init__(self, model, controller, parent = None): Frame.__init__(self, parent) self.model = model self.controller = controller self.controller.setView(self) self.createBoard( ) panel = Frame(self) panel.pack(side = BOTTOM, fill = X) Button(panel, text = "Bagong Laro", command = self.controller.startNewGame).pack(side = RIGHT) self.mineCount = StringVar (panel) self.mineCount.set(self.model.mineCount) Spinbox(panel, from_ = MIN_MINE_COUNT, to = MAX_MINE_COUNT, textvariable = self.mineCount, width = 5).pack(side = RIGHT) Label(panel, text = " Bilang ng mga minuto: ").pack(side = RIGHT) self.rowCount = StringVar(panel) self.rowCount.set(self.model.rowCount) Spinbox(panel, from_ = MIN_ROW_COUNT, to = MAX_ROW_COUNT, textvariable = self. rowCount , lapad = 5).pack(side = RIGHT) Label(panel, text = " x ").pack(side = RIGHT) self.columnCount = StringVar(panel) self.columnCount.set(self.model.columnCount) Spinbox (panel, from_ = MIN_COLUMN_COUNT, to = MAX_COLUMN_COUNT, textvariable = self.columnCount, width = 5).pack(side = RIGHT) Label(panel, text = "Laki ng field: ").pack(side = RIGHT) def syncWithModel ( sarili): para sa row sa range(self.model.rowCount): para sa column sa range(self.model.columnCount): cell = self.model.getCell(row, column) kung cell: btn = self.buttonsTable[ row ] [ column ] kung self.model.isGameOver() at cell.mined: btn.config(bg = "black", text = "") kung cell.state == "closed": btn.config(text = "" ) elif cell.state == "binuksan": btn.config(relief = SUNKEN, text = "") kung cell.counter > 0: btn.config(text = cell.counter) elif cell.mined: btn.config( bg = "pula") elif cell.state == "na-flagged": btn.config(text = "P") elif cell.state == "questioned": btn.config(text = "?") def blockCell(self , row, column, block = True): btn = self.buttonsTable[ row ][ column ] kung hindi btn: return if block: btn.bind("
Ang aming View ay batay sa klase ng Frame mula sa tkinter module, kaya siguraduhing patakbuhin ang naaangkop na command sa pag-import: mula sa tkinter import * . Ipinapasa ng tagabuo ng klase ang Model at Controller. Ang paraan ng createBoard() ay agad na tinatawag upang ilatag ang playing field mula sa mga cell. Hayaan akong sabihin nang maaga na para sa layuning ito gagamitin namin ang mga regular na pindutan ng Pindutan. Pagkatapos ay gagawa ng Frame, na magsisilbing panel sa ibaba para sa pagtukoy ng mga parameter ng laro. Sa panel na ito ay sunud-sunod naming inilalagay ang button na "Bagong Laro", ang tagapangasiwa nito ay ang aming Controller kasama ang pamamaraang startNewGame() nito, at pagkatapos ay tatlong Spinbox counter upang matukoy ng manlalaro ang laki ng playing field at ang bilang ng mga mina.
Ang paraan ng syncWithModel() ay nag-loop nang dalawang beses sa bawat cell ng laro at binabago ang hitsura ng button na kumakatawan dito sa aming GUI nang naaayon. Para sa pagiging simple, gumamit ako ng mga simbolo ng teksto upang ipakita ang mga simbolo, ngunit hindi ganoon kahirap baguhin ang teksto sa mga graphic mula sa mga panlabas na graphics file.
Gayundin, tandaan na ginagamit namin ang estilo ng SUNKEN na pindutan upang kumatawan sa bukas na cell. At sa kaso ng pagkawala, binubuksan namin ang lokasyon ng lahat ng mga mina sa larangan ng paglalaro, na ipinapakita ang kaukulang mga pindutan sa itim, at i-highlight ang pindutan na naaayon sa huling bukas na cell na may isang minahan na pula:
Ang sumusunod na blockCell() na paraan ay nagsisilbing pansuportang papel at nagbibigay-daan sa controller na itakda ang estado ng pagharang ng mga button. Ito ay upang maiwasan ang na-flag na mga cell ng laro na hindi sinasadyang mabuksan, at nakakamit sa pamamagitan ng pagtatakda ng isang walang laman na left-click na handler.
Ang getGameSettings() method ay nagbabalik lamang ng mga halaga ng mga counter na matatagpuan sa ibabang panel na may sukat ng playing field at ang bilang ng mga mina.
Ang paggawa ng representasyon ng playing field ay ginagawa sa createBoard() na paraan. Una sa lahat, sinusubukan naming tanggalin ang lumang playing field, kung mayroon ito, at sinusubukan din naming itakda ang mga counter value mula sa panel alinsunod sa kasalukuyang configuration ng Model. Isang bagong Frame ang gagawa, na tatawagin nating board, upang kumatawan sa game board. Binubuo namin ang talahanayan ng mga buttonsTable ayon sa parehong prinsipyo tulad ng mga cell ng laro sa Model gamit ang isang double loop. Ang mga humahawak ng bawat button ay nakatali sa mga pamamaraan ng onLeftClick() at onRightClick() ng Controller para sa pag-click sa kaliwa at kanang mga pindutan ng mouse, ayon sa pagkakabanggit.
Ang huling dalawang paraan showWinMessage() at showGameOverMessage() ay nagpapakita lamang ng mga dialog box na may kaukulang mga mensahe gamit ang showinfo() function. Upang magamit ito, kakailanganin mong mag-import ng isa pang module: mula sa tkinter.messagebox import * .
Controller ng MinesweeperController
Ngayon ay nakarating na tayo sa pagpapatupad ng Controller:
Class MinesweeperController: def __init__(self, model): self.model = model def setView(self, view): self.view = view def startNewGame(self): gameSettings = self.view.getGameSettings() subukan: self.model. startGame(*map(int, gameSettings)) maliban sa: self.model.startGame(self.model.rowCount, self.model.columnCount, self.model.mineCount) self.view.createBoard() def onLeftClick(self, row, column): self.model.openCell(row, column) self.view.syncWithModel() kung self.model.isWin(): self.view.showWinMessage() self.startNewGame() elif self.model.isGameOver(): self.view.showGameOverMessage() self.startNewGame() def onRightClick(self, row, column): self.model.nextCellMark(row, column) self.view.blockCell(row, column, self.model.getCell(row, column).state == "na-flag") self.view.syncWithModel()
Upang itali ang View sa Controller, idinagdag namin ang setView() method. Ito ay dahil kung gusto naming ipasa ang isang View sa constructor, ang View na ito ay dapat na umiiral na bago nilikha ang Controller. At pagkatapos ang isang katulad na solusyon na may karagdagang paraan para sa pagbubuklod ay lilipat lamang mula sa Controller patungo sa View, kung saan lilitaw ang setController() na paraan.
Ang paraan ng handler para sa pag-click sa pindutan ng Bagong Laro, startNewGame(), ay unang humihiling ng mga parameter ng laro na ipinasok sa View. Ang mga parameter ng laro ay ibinalik bilang isang tuple ng tatlong bahagi, na sinusubukan naming i-convert sa int . Kung magiging maayos ang lahat, ipapasa namin ang mga halagang ito sa paraan ng StartGame() ng Modelo para bumuo ng playing field. Kung may mali, gagawa lang kami ng playing field gamit ang mga lumang parameter. Sa wakas, humiling kami na lumikha ng bagong display ng game board sa View sa pamamagitan ng pagtawag sa createBoard() na paraan.
Ang onLeftClick() handler ay unang nagsasabi sa Modelo na buksan ang cell ng laro sa napiling posisyon ng player. Pagkatapos ay ipinaalam nito sa View na ang estado ng Modelo ay nagbago at nag-aalok na i-redraw ang lahat. Pagkatapos ay susuriin ang Modelo para sa tagumpay o pagkatalo. Kung mangyari ang alinman sa mga ito, ang isang kahilingan ay unang ipapadala sa View upang ipakita ang naaangkop na abiso, at pagkatapos ay ang startNewGame() handler ay tatawag upang magsimula ng isang bagong laro.
Ang right-click ay pinangangasiwaan sa onRightClick() na paraan. Tinatawag ng unang linya ang pamamaraan ng Model's nextCellMark() upang paikot na baguhin ang marka ng napiling cell ng laro. Depende sa bagong estado ng cell, isang kahilingan ang ipapadala sa View upang itakda o alisin ang lock sa kaukulang button. At sa dulo, ang View ay muling na-update upang ipakita ang kasalukuyang estado ng Modelo.
Pinagsasama-sama ang Modelo, View at Controller
Ngayon ang natitira na lang ay ikonekta ang lahat ng elemento sa loob ng aming pagpapatupad ng Minesweeper batay sa pattern ng MVC at ilunsad ang laro:
Modelo = MinesweeperModel() controller = MinesweeperController(model); view = MinesweeperView(modelo, controller) view.pack() view.mainloop()
Konklusyon
Kaya tiningnan namin ang pattern ng MVC. Dumaan tayo sa teorya nang maikli. At pagkatapos ay gumawa kami ng isang ganap na application sa paglalaro ng hakbang-hakbang, mula sa pahayag ng problema at disenyo ng arkitektura hanggang sa pagpapatupad sa wikang programming ng Python gamit ang tkinter graphical module.
Well, una, siyempre, ito ay isang modelo o, sa madaling salita, isang diskarte sa pagbuo ng mga website at application sa Internet, at pangalawa, ang MVC ay ginagamit upang mapabuti ang kahusayan ng mga system na binuo, pati na rin upang matiyak ang seguridad. at paglaban sa pag-hack.
Ang ibig sabihin ng MVC modelo-tingnan-controller at maaaring literal na isalin bilang Model-View-Controller.
Sa kabila ng katotohanan na ang modelo ng pag-unlad ay tila bago, matagal na itong napatunayan at malawakang ginagamit sa pagbuo ng mga website, kabilang ang. Ang konsepto ng MVC ay unang inilarawan ni Trygve Reenskaug noong 1979.
Konsepto ng MVC o kung ano ang binubuo nito
MVC model may kasamang tatlong bahagi: Modelo, View at Controller.
Ang pinakamahalagang bagay dito, siyempre, ay ang Modelo. Ang modelo ay isang hanay ng mga pamamaraan at algorithm para sa pagproseso ng data. Ang Modelo mismo ay hindi naglalaman ng data, ngunit bilang panuntunan, kinukuha ito mula sa database at pinoproseso ito ayon sa mga paunang natukoy na algorithm. Kung pag-uusapan natin ang tungkol sa pag-unlad ng Web, ang modelo ay maglalaman ng isang hanay ng mga klase at function, halimbawa sa PHP.
Ang pangalawang elemento ay ang View. Binibigyang-daan kang magpakita ng impormasyon. Kung ito ay isang website, ang impormasyon ay ipinapakita sa browser. Kapag bumubuo ng mga website, ang isang view ay naglalaman ng HTML code kung saan ang mga variable ay pinapalitan, na kinuha hindi mula sa modelo, ngunit mula sa controller.
Kaya, ang ikatlong elemento ay ang Controller. Ang pangunahing tungkulin nito ay upang magbigay ng komunikasyon sa pagitan ng gumagamit at ng modelo. Maaari ring maglaman ng PHP code.
Maraming mga baguhan na developer ang hindi gumagamit ng Modelo o ginagamit lamang ito para ma-access ang database, na isang malaking pagkakamali, hindi tama at salungat sa lohika ng MVC model. At lahat ng pangunahing code ay inilalagay sa Controller. Ang mga kahihinatnan nito ay, una, isang malaking code, at pangalawa, mas mababang bilis ng aplikasyon. Well, ang paggawa ng mga pagbabago sa code ng naturang mga application ay mas mahirap.
Halimbawa, isaalang-alang ang modelo ng MVC para sa isang site ng balita
Ang gumagamit ay nakarating sa isang pahina ng website. Siya ay ipinapakita ng isang default na pahina na may isang tiyak na listahan ng mga balita. Ang impormasyon para sa pagbuo ng balita ay kinuha mula sa database.
Kapag nag-type ang user ng isang partikular na page sa address bar ng browser, ipapasa ang kahilingan sa controller, at inilunsad ang isang function na nagpoproseso nito at naglo-load ng Modelo.
Ang modelo, na nakatanggap ng kinakailangang data, na naproseso na at nakapangkat sa isang bagay o array, ay bubuo ng isang query sa database. Ang pagkakaroon ng natanggap na data, ang Modelo ay bumubuo nito sa isang tiyak na anyo at inililipat ito sa Controller, at pagkatapos ay ililipat ito sa View. Hindi magiging error kung direktang ipinasa ang data sa View. Ngunit muli, ginagamit ang mga hindi kinakailangang aksyon, na sa huli ay humahantong sa mas mahabang oras ng paglo-load para sa site at mga application.
Ang view, na nakatanggap ng array o object na may balita, ay naglo-load ng ilang partikular na HTML code, CSS, kung kinakailangan, at ipinapakita ng javascript ang lahat ng ito sa user.
Ginagamit ang modelong ito sa maraming control system at frameworks, isa na rito ang CodeIgniter, na kamakailan ay nagkaroon ng bagong buhay.
Kaya, gamit ang modelo ng MVC, madali kang makakagawa ng isang sistema ng pangangasiwa para sa isang website o aplikasyon sa Internet. Kaya ang balangkas ng CodeIgniter ay gumagamit ng eksaktong modelong ito.
Magandang hapon, mahal na mga kasamahan. Sa artikulong ito gusto kong pag-usapan ang tungkol sa aking analytical na pag-unawa sa mga pagkakaiba sa pagitan ng mga pattern ng MVC, MVP at MVVM. Na-prompt akong isulat ang artikulong ito sa pamamagitan ng pagnanais na maunawaan ang mga modernong diskarte sa pagbuo ng malaking software at ang kaukulang mga tampok ng arkitektura. Sa kasalukuyang yugto ng aking career ladder, hindi ako direktang developer, kaya maaaring naglalaman ang artikulo ng mga error, kamalian at hindi pagkakaunawaan. Naiintriga sa kung paano nakikita ng mga analyst kung ano ang ginagawa ng mga programmer at arkitekto? Pagkatapos ay maligayang pagdating sa pusa.
Mga link
Ang unang bagay na gusto kong simulan ay ang mga link sa mga panlabas na materyales na gumabay sa akin sa proseso ng pagsulat ng artikulong ito:Panimula
Sa panahong mas maliwanag ang araw at mas luntian ang damo, ang isang pangkat ng mga mag-aaral na tulad ng may-akda ng artikulong ito ay bumuo ng software sa pamamagitan ng pagsulat ng daan-daang linya ng code nang direkta sa interface ng produkto. Minsan ginagamit ang mga serbisyo at tagapamahala upang gumana sa data, at pagkatapos ay nakuha ang solusyon gamit ang pattern ng Document-View. Ang pagsuporta sa naturang code ay nangangailangan ng napakalaking gastos, dahil ang isang bagong developer ay kailangang sanayin (sabihin) kung anong code ang may pananagutan para sa kung ano ang nasa produkto, at walang usapan tungkol sa anumang pagsubok sa yunit. Ang development team ay 4 na tao na nakaupo sa isang silid.Lumipas ang oras, nagbago ang trabaho. Ang mga application na binuo ay naging mas malaki at mas kumplikado, at mula sa isang cohesive team ng mga developer nagkaroon ng maraming iba't ibang mga team ng mga developer, arkitekto, usability specialist, designer at PM. Ngayon lahat ay responsable para sa kanilang sariling lugar: GUI, lohika ng negosyo, mga bahagi. Lumitaw ang isang departamento ng pagsusuri, pagsubok, at arkitektura. Ang halaga ng software development ay tumaas ng daan-daan at kahit libu-libong beses. Ang diskarte na ito sa pag-unlad ay nangangailangan ng isang matatag na arkitektura na mag-synchronize ng iba't ibang functional na bahagi ng produkto sa bawat isa.
Mga pattern
Dahil sa layuning bawasan ang mga gastos sa paggawa para sa pagbuo ng kumplikadong software, ipinapalagay namin na kinakailangang gumamit ng mga handa na pinag-isang solusyon. Pagkatapos ng lahat, pinapadali ng mga template na aksyon ang komunikasyon sa pagitan ng mga developer, nagbibigay-daan sa iyong sumangguni sa mga kilalang disenyo, at bawasan ang bilang ng mga error.Ayon sa Wikipedia, ang isang pattern ng disenyo ay isang paulit-ulit na disenyo ng arkitektura na kumakatawan sa isang solusyon sa isang problema sa disenyo sa loob ng ilang madalas na nangyayaring konteksto.
Magsimula tayo sa unang pangunahing bagay - Model-View-Controller. Ang MVC ay isang pangunahing pattern na nakahanap ng paraan sa maraming teknolohiya, na nagbunga ng mga bagong teknolohiya, at ginagawang mas madali ang buhay para sa mga developer araw-araw.
Ang pattern ng MVC ay unang lumitaw sa wikang SmallTalk. Kinailangan ng mga developer na makabuo ng solusyon sa arkitektura na maghihiwalay sa graphical na interface mula sa lohika ng negosyo, at sa lohika ng negosyo mula sa data. Kaya, sa klasikong bersyon nito, ang MVC ay binubuo ng tatlong bahagi, na nagbibigay ng pangalan nito. Tingnan natin sila:
Modelo
Ang isang Modelo ay karaniwang nauunawaan bilang isang bahagi na naglalaman ng functional na lohika ng negosyo ng isang application. Ang modelo ay dapat na ganap na independyente mula sa natitirang bahagi ng produkto. Ang layer ng modelo ay hindi kailangang malaman ang anumang bagay tungkol sa mga elemento ng disenyo o kung paano ito ire-render. Nakamit ang isang resulta na nagbibigay-daan sa iyong baguhin ang presentasyon ng data, kung paano ipinapakita ang mga ito, nang hindi hinahawakan ang mismong Modelo.Ang modelo ay may mga sumusunod na tampok:
- Ang modelo ay ang lohika ng negosyo ng aplikasyon;
- Ang modelo ay may kaalaman tungkol sa sarili nito at hindi alam ang tungkol sa mga controller at view;
- Para sa ilang mga proyekto, ang modelo ay simpleng layer ng data (DAO, database, XML file);
- Para sa iba pang mga proyekto, ang modelo ay isang database manager, isang set ng mga bagay, o simpleng application logic;
Tingnan
Kasama sa mga responsibilidad ng View ang pagpapakita ng data na natanggap mula sa Modelo. Gayunpaman, hindi direktang makakaimpluwensya ang view sa modelo. Masasabi nating ang isang view ay may read-only na access sa data.Ang representasyon ay may mga sumusunod na katangian:
- Ang view ay nagpapatupad ng pagpapakita ng data na nakuha mula sa modelo sa anumang paraan;
- Sa ilang mga kaso, ang view ay maaaring may code na nagpapatupad ng ilang lohika ng negosyo.
Mga pagkakaiba sa pagitan ng MVP at MVVM at MVP
Ang pinakakaraniwang uri ng pattern ng MVC ay:- Model-View-Controller
- Model-View-Presenter
- Model-View-View na Modelo
Isaalang-alang at ihambing natin ang bawat isa sa kanila.
Model-View-Presenter
Binibigyang-daan ka ng diskarteng ito na lumikha ng abstraction ng representasyon. Upang gawin ito, kailangan mong pumili ng isang view interface na may isang tiyak na hanay ng mga katangian at pamamaraan. Ang nagtatanghal, sa turn, ay tumatanggap ng reference sa pagpapatupad ng interface, nag-subscribe sa mga kaganapan sa pagtatanghal, at binabago ang modelo kapag hiniling.
Mga palatandaan ng isang nagtatanghal:
- Direktang nakikipag-ugnayan ang view sa nagtatanghal sa pamamagitan ng pagtawag ng mga naaangkop na function o kaganapan sa halimbawa ng nagtatanghal;
- Nakikipag-ugnayan ang Presenter sa View sa pamamagitan ng paggamit ng isang espesyal na interface na ipinatupad ng View;
- Ang isang instance ng presenter ay nauugnay sa isang display.
Pagpapatupad:
Dapat ipatupad ng bawat view ang kaukulang interface. Tinutukoy ng interface ng pagtatanghal ang hanay ng mga function at kaganapan na kailangan upang makipag-ugnayan sa user (halimbawa, IView.ShowErrorMessage(string msg)). Ang nagtatanghal ay dapat magkaroon ng isang sanggunian sa pagpapatupad ng kaukulang interface, na karaniwang ipinapasa sa constructor.
Ang lohika ng pagtatanghal ay dapat na may sanggunian sa halimbawa ng nagtatanghal. Ang lahat ng mga kaganapan sa view ay ipinapasa sa nagtatanghal para sa pagproseso at halos hindi naproseso ng lohika ng pagtatanghal (kabilang ang paglikha ng iba pang mga view).
Halimbawa ng paggamit: Mga Windows Form.
Model-View-View na Modelo
![](https://i2.wp.com/habrastorage.org/getpro/habr/post_images/3a3/8f6/589/3a38f65895eec8816ccacb3388de200a.png)
Binibigyang-daan ka ng diskarteng ito na iugnay ang mga elemento ng view sa mga katangian at kaganapan ng modelo ng View. Ito ay maaaring argued na ang bawat layer ng pattern na ito ay hindi alam tungkol sa pagkakaroon ng isa pang layer.
Mga tampok ng modelo ng View:
- Dalawang-daan na komunikasyon sa pagtatanghal;
- Ang modelo ng view ay isang abstraction ng isang view. Karaniwang nangangahulugan na ang mga katangian ng view ay kapareho ng mga katangian ng View/Model
- Ang modelo ng View ay walang reference sa view interface (IView). Awtomatikong binabago ng pagbabago sa estado ng modelo ng View ang view at kabaliktaran, dahil ginagamit ang mekanismo ng pagbubuklod ng data (Bindings)
- Ang isang instance ng isang View na modelo ay nauugnay sa isang view.
Pagpapatupad:
Kapag ginagamit ang pattern na ito, ang view ay hindi nagpapatupad ng kaukulang interface (IView).
Ang view ay dapat may link sa data source (DataContex), na sa kasong ito ay ang View model. Ang mga elemento ng view ay nakatali sa mga kaukulang katangian at kaganapan ng modelo ng View.
Sa turn, ang modelo ng View ay nagpapatupad ng isang espesyal na interface na ginagamit upang awtomatikong i-update ang mga elemento ng view. Ang isang halimbawa ng naturang interface sa WPF ay INotifyPropertyChanged.
Halimbawa ng paggamit: WPF
Model-View-Controller
![](https://i0.wp.com/habrastorage.org/getpro/habr/post_images/01c/c4f/3f2/01cc4f3f2646eaea356bc50dccce40d6.png)
Ang pangunahing ideya ng pattern na ito ay ang parehong controller at ang view ay nakasalalay sa modelo, ngunit ang modelo ay hindi nakasalalay sa dalawang bahagi na ito.
Mga katangian ng controller
- Tinutukoy ng controller kung aling view ang dapat ipakita sa sandaling ito;
- Ang mga kaganapan sa view ay maaari lamang makaapekto sa controller Ang controller ay maaaring makaapekto sa modelo at tumukoy ng isa pang view.
- Maramihang mga view ay posible para sa isang controller lamang;
Pagpapatupad:
Ang controller ay humarang sa kaganapan mula sa labas at, alinsunod sa logic na naka-embed dito, ay tumutugon sa kaganapang ito sa pamamagitan ng pagpapalit ng Modelo sa pamamagitan ng pagtawag sa naaangkop na pamamaraan. Pagkatapos ng pagbabago, ginagamit ng Modelo ang kaganapang binago nito, at lahat ng mga kaganapan sa View na naka-subscribe dito, pagkatanggap nito, bumaling sa Modelo para sa na-update na data, pagkatapos nito ay ipapakita ang mga ito.
Halimbawa ng paggamit: MVC ASP.NET
Ipagpatuloy
Ang pagpapatupad ng mga pattern ng MVVM at MVP, sa unang tingin, ay mukhang medyo simple at katulad. Gayunpaman, para sa MVVM ang pagbubuklod ng view sa View-modelo ay awtomatikong ginagawa, ngunit para sa MVP kinakailangan na mag-programMukhang may higit na kontrol ang MVC sa view.
Pangkalahatang mga patakaran para sa pagpili ng isang pattern
MVVM
- Ginamit sa isang sitwasyon kung saan ang data binding ay posible nang hindi na kailangang magpakilala ng mga espesyal na interface ng view (ibig sabihin, hindi na kailangang ipatupad ang IView);
- Ang isang karaniwang halimbawa ay ang teknolohiya ng WPF.
MVP
- Ginagamit sa isang sitwasyon kung saan ang data binding ay hindi posible (Binding ay hindi maaaring gamitin);
- Ang isang karaniwang halimbawa ay ang paggamit ng Windows Forms.
MVC
- Ginagamit sa isang sitwasyon kung saan hindi posible ang komunikasyon sa pagitan ng view at iba pang bahagi ng application (at hindi mo magagamit ang MVVM o MVP);
- Ang isang karaniwang kaso ng paggamit ay ASP.NET MVC.
Konklusyon
Sa konklusyon, nais ipahiwatig ng may-akda ng artikulong ito na ang mahigpit na pagsunod sa isang pattern ay hindi palaging ang pinakamahusay na pagpipilian. Halimbawa, isipin na gusto mong gamitin ang MVVM upang bumuo ng mga application gamit ang Windows Forms sa pamamagitan ng Bindings property ng mga kontrol. Ang iyong layunin ay paghiwalayin ang presentasyon mula sa lohika ng negosyo at ang lohika na nag-uugnay sa kanila. Ang application ay dapat na madaling subukan at suportahan, at nauunawaan para sa mga analyst (pagkatapos ng lahat, sa tanong na "paano nasusukat ang pagganap ng isang hard drive" mayroon lamang isang tamang sagot - sa Joules (abstract na halimbawa Model -> Views)) .Maraming salamat sa iyong oras, enjoy reading!
Ang konsepto ng MVC (Model-View-Controller) ay madalas na binabanggit sa mundo ng web programming sa mga nakaraang taon. Ang bawat isa na sa anumang paraan ay konektado sa pagbuo ng web application ay nakatagpo ng acronym na ito sa isang paraan o iba pa. Ngayon ay mauunawaan natin kung ano ang konsepto ng MVC at kung bakit ito naging tanyag.
Sinaunang kasaysayan
Ang MVC ay hindi isang pattern ng proyekto, ito ay isang pattern ng disenyo na naglalarawan kung paano bumuo ng istraktura ng aming aplikasyon, ang mga responsibilidad at pakikipag-ugnayan ng bawat isa sa mga bahagi sa istrukturang ito.
Una itong inilarawan noong 1979, siyempre, para sa ibang kapaligiran. Walang konsepto ng web application noon. Inihasik ni Tim Berners Lee ang mga buto World Wide Ang Web (WWW) ay lumitaw noong unang bahagi ng nineties at binago ang mundo magpakailanman. Ang template na ginagamit namin ngayon ay isang adaptasyon ng orihinal na template para sa web development.
Ang pagiging popular ng istrukturang ito sa mga web application ay dahil sa pagsasama nito sa dalawang development environment na naging napakasikat: Struts at Ruby on Rails. Ang dalawang development environment na ito ay nagtatakda ng landas para sa daan-daang development environment na ginawa sa ibang pagkakataon.
MVC para sa mga web application
Ang ideya sa likod ng pattern ng disenyo ng MVC ay napaka-simple: kailangan naming malinaw na paghiwalayin ang mga responsibilidad para sa iba't ibang paggana sa aming mga application:
Ang application ay nahahati sa tatlong pangunahing bahagi, ang bawat isa ay may pananagutan para sa iba't ibang gawain. Tingnan natin ang mga bahagi nang detalyado gamit ang isang halimbawa.
Controller
Controller namamahala sa mga kahilingan ng user (natatanggap bilang HTTP GET o POST na mga kahilingan kapag nag-click ang user sa mga elemento ng interface upang gumanap iba't ibang aksyon). Ang pangunahing pag-andar nito ay upang tawagan at i-coordinate ang pagkilos ng mga kinakailangang mapagkukunan at mga bagay na kinakailangan upang maisagawa ang mga aksyon na tinukoy ng gumagamit. Karaniwang tinatawag ng controller ang naaangkop na modelo para sa gawain at pinipili angkop na hitsura.
Modelo
Modelo- Ito ang mga data at panuntunan na ginagamit upang gumana sa data na kumakatawan sa konsepto ng pamamahala ng application. Sa anumang aplikasyon, ang buong istraktura ay na-modelo bilang data na pinoproseso sa isang tiyak na paraan. Ano ang isang user para sa isang application - isang mensahe o isang libro? Ang data lamang na dapat iproseso ayon sa mga panuntunan (ang petsa ay hindi maaaring tumuro sa hinaharap, ang email ay dapat nasa isang tiyak na format, ang pangalan ay hindi maaaring mas mahaba kaysa sa X na mga character, at iba pa).
Ang modelo ay nagbibigay sa controller ng representasyon ng data na hiniling ng user (mensahe, pahina ng libro, photo album, atbp.). Magiging pareho ang modelo ng data kahit paano natin ito gustong ipakita sa user. Kaya pipili kami ng anuman naa-access na view upang ipakita ang data.
Ang modelo ay naglalaman ng pinakamahalagang bahagi ng lohika ng aming aplikasyon, ang lohika na lumulutas sa problemang kinakaharap namin (forum, tindahan, bangko, atbp.). Ang controller ay karaniwang naglalaman ng lohika ng organisasyon para sa application mismo (katulad ng housekeeping).
Tingnan
Tingnan nagbibigay iba't ibang paraan representasyon ng data na nakuha mula sa modelo. Maaari itong maging isang template na puno ng data. Maaaring may iba't ibang uri, at pipiliin ng controller kung alin ang angkop sa pinakamahusay na posibleng paraan para sa kasalukuyang sitwasyon.
Ang isang web application ay karaniwang binubuo ng isang hanay ng mga controller, modelo, at view. Ang controller ay maaaring idisenyo bilang isang pangunahing controller na tumatanggap ng lahat ng mga kahilingan at tumatawag sa iba pang mga controller upang magsagawa ng mga aksyon depende sa sitwasyon.
Tingnan natin ang isang halimbawa
Sabihin nating kailangan nating bumuo ng online bookstore. Ang gumagamit ay maaaring gumanap susunod na hakbang: tingnan ang mga libro, magparehistro, bumili, magdagdag ng mga item sa kasalukuyang order, lumikha o magtanggal ng mga libro (kung siya ay isang administrator). Tingnan natin kung ano ang mangyayari kapag nag-click ang isang user sa isang kategorya pantasya upang tingnan ang mga pamagat ng mga aklat na magagamit sa aming tindahan.
Mayroon kaming partikular na controller para pangasiwaan ang lahat ng pagkilos na nauugnay sa mga aklat (tingnan, i-edit, gumawa, atbp.). Tawagan natin books_controller.php sa ating halimbawa. Gayundin kailangan namin ng isang modelo tulad ng book_model.php, na nagpoproseso ng data at lohika na nauugnay sa isang item sa tindahan. Sa konklusyon, kailangan namin ng ilang view upang kumatawan sa data, tulad ng isang listahan ng libro, isang pahina sa pag-edit, at iba pa.
Ipinapakita ng sumusunod na figure kung paano pinoproseso ang kahilingan ng isang user na tingnan ang isang listahan ng mga aklat sa isang paksa pantasya:
Ang controller (books_controller.php) ay tumatanggap ng kahilingan ng user ( Kahilingan sa HTTP GET o POST). Maaari tayong lumikha ng sentral na controller, halimbawa index.php, na tumatanggap ng kahilingan at tumatawag sa books_controller.php.
Sinusuri ng controller ang kahilingan at mga parameter at pagkatapos ay tinatawagan ang modelo(book_model.php), nagtatanong mayroon siyang listahan ng mga available na libro sa paksa pantasya .
Ang modelo ay tumatanggap ng data mula sa database (o isa pang mapagkukunan na nag-iimbak ng impormasyon), naglalapat ng mga filter at ang kinakailangang lohika, at pagkatapos ay nagbabalik ng data na kumakatawan sa isang listahan ng mga aklat.
Ginagamit ng controller ang naaangkop na view upang ipakita ang data sa user. Kung ang kahilingan ay kasama mobile phone, ginagamit ang view ng mobile phone; kung ang gumagamit ay gumagamit ng isang tiyak na disenyo ng interface, pagkatapos ay ang kaukulang view ay pinili, at iba pa.
Ano ang mga pakinabang?
Ang pinaka-halatang benepisyo na makukuha natin sa paggamit ng konsepto ng MVC ay ang malinaw na paghihiwalay ng lohika ng presentasyon (user interface) at lohika ng aplikasyon.
Sinusuportahan ang iba't ibang uri ng mga gumagamit na gumagamit iba't ibang uri mga device ay karaniwang problema ating mga araw. Ang interface na ibinigay ay dapat na iba kung ang kahilingan ay kasama personal na computer o mula sa isang mobile phone. Ang modelo ay nagbabalik ng parehong data, ang pagkakaiba lamang ay pinipili ng controller iba't ibang uri upang mag-output ng data.
Bilang karagdagan sa paghihiwalay ng mga view mula sa lohika ng aplikasyon, ang konsepto ng MVC ay makabuluhang binabawasan ang pagiging kumplikado malalaking aplikasyon. Ang code ay lumalabas na mas nakabalangkas, at sa gayon ay ginagawang mas madali ang pagsuporta, pagsubok at muling gamitin mga desisyon.
Bakit gamitin ang kapaligiran sa trabaho?
Kapag ginagamit mo ang workbench, pangunahing istraktura Ang MVC ay handa na at ang kailangan mo lang gawin ay palawakin ang istraktura sa pamamagitan ng paglalagay ng iyong mga file sa naaangkop na mga direktoryo upang tumugma sa MVC pattern. Dagdag pa, magkakaroon ka ng isang hanay ng mga tampok na nakasulat na at mahusay na nasubok.
Kunin natin ang cakePHP bilang isang halimbawa kapaligiran sa pagtatrabaho MVC. Pagkatapos ng pag-install magkakaroon ka ng tatlong pangunahing direktoryo:
- cake/
- mga nagtitinda/
Folder app ay kung saan matatagpuan ang iyong mga file. Ito ang lugar para bumuo ng iyong bahagi ng application.
Sa isang folder cake Ang mga cakePHP file (workbench functionality) ay naka-host.
Folder mga nagtitinda ginagamit upang mag-imbak ng mga aklatan PHP third party mga developer.
sa iyo workspace(direktoryo ng app) ay may sumusunod na istraktura:
- app/
- config/
- controllers/
- lokal/
- mga modelo/
- mga plugin/
- pagsubok/
- mga nagtitinda/
- view/
- webroot/
Kailangan mong ilagay ang iyong mga controllers sa isang direktoryo mga controllers, mga modelo sa direktoryo mga modelo at mga uri sa direktoryo mga pananaw!
Sa sandaling simulan mo nang gamitin ang workbench, agad itong magiging malinaw kung saan naninirahan ang halos bawat bahagi ng iyong application na kailangang gawin o baguhin. Ang organisasyong ito mismo ay lubos na nagpapasimple sa proseso ng pagbuo at pagpapanatili ng isang aplikasyon.
Gamit ang workbench para sa aming halimbawa
kasi ang araling ito ay hindi nilayon upang ipakita ang proseso ng paglikha ng isang application gamit ang cakePHP, ipapakita lamang namin ang code para sa modelo, controller at view na may mga komento tungkol sa mga benepisyo ng paggamit ng MVC workbench. Ang code ay sadyang pinasimple at hindi angkop para sa paggamit sa isang tunay na aplikasyon.
Tandaan, tumitingin kami sa isang bookstore at isang curious na user na gustong makakita buong listahan mga libro sa paksa pantasya. Natanggap ng controller ang kahilingan ng user at inayos ang mga kinakailangang aksyon.
Kaya, sa sandaling na-click ng user ang button, hinihiling ng browser ang ibinigay na url:
www.ourstore.com/books/list/fantasy
Ang CakePHP ay nag-format ng URL gamit ang isang pattern /controller/action/param1/param2, Saan aksyon ay isang function na tinatawag ng controller. Sa luma klasikong anyo magiging ganito ang url:
www.ourstore.com/books_controller.php?action=list&category=fantasy
Controller
Sa kapaligirang nagtatrabaho ng cakePHP, magiging ganito ang hitsura ng aming controller:
pinapalawak ng klase BooksController ang AppController (
Listahan ng function($category) (
$this->set("books", $this->Book->findAllByCategory($category));
Function add() ( ... ... )
Function delete() ( ... ... )
... ... } ?>
Simple, hindi ba? Ang controller na ito ay maliligtas bilang books_controller.php at nai-post sa /app/controllers. Naglalaman ito ng listahan ng mga function na nagsasagawa ng mga aksyon para sa aming halimbawa, pati na rin ang iba pang mga function upang magsagawa ng mga operasyong nauugnay sa libro (idagdag bagong libro, magtanggal ng aklat, at iba pa).
Ang kapaligiran sa trabaho ay nagbibigay sa atin ng marami handa na mga solusyon at kailangan mo lang gumawa ng listahan ng mga libro. Mayroong isang batayang klase na tumutukoy na sa pangunahing pag-andar ng controller, kaya kailangan mong magmana ng mga katangian at pag-andar ng klase na ito ( AppController ay ang tagapagmana Controller).
Ang kailangan mo lang gawin sa listahan ng aksyon ay tawagan ang modelo para makuha ang data at pagkatapos ay pumili ng view para ipakita ito sa user. Narito kung paano ito ginawa.
ito->Aklat- ito ang aming modelo, at bahagi ng code:
$this->Book->findAllByCategory($category)
nagsasabi sa modelo na magbalik ng listahan ng mga aklat sa napiling paksa (titingnan natin ang modelo sa ibang pagkakataon).
Pamamaraan itakda sa linya:
$this->set("books", $this->Book->findAllByCategory($category));
Ang controller ay nagpapasa ng data sa view. Variable mga libro tinatanggap ang data na ibinalik ng modelo at ginagawa itong available sa view.
Ngayon ang lahat na natitira ay upang ipakita ang view, ngunit ang function na ito ay awtomatikong ginagawa sa cakePHP kung gagamitin namin ang default na view. Kung gusto nating gumamit ng ibang uri, dapat nating tahasang tawagan ang pamamaraan render.
Modelo
Ang modelo ay mas simple:
pinalawak ng class Book ang AppModel (
Bakit walang laman? Dahil siya ang tagapagmana batayang klase, na nagbibigay ng kinakailangang functionality at kailangan naming gamitin ang convention ng pagpapangalan ng CakePHP upang matiyak na awtomatikong pinangangasiwaan ng workbench ang lahat ng iba pang gawain. Halimbawa, alam ng cakePHP batay sa pangalan na modelong ito ginamit sa BooksController, at mayroon itong access sa isang database table na pinangalanang mga libro.
Sa kahulugang ito, magkakaroon tayo ng isang modelo na maaari lamang magbasa, magtanggal, o mag-save ng data sa database.
I-save ang code bilang libro.php sa isang folder /app/models.
Tingnan
Ang kailangan lang nating gawin ngayon ay lumikha ng view (ni kahit man lang, isa) para sa isang listahan ng mga aksyon. Ang view ay magkakaroon ng HTML code at ilang (kaunti hangga't maaari) na mga linya ng PHP code upang i-loop sa hanay ng mga aklat na ibinibigay ng modelo.
Pangalan | May-akda | Presyo |
---|---|---|
Tulad ng nakikita mo, ang view ay hindi lumilikha ng isang buong pahina, ngunit isang fragment lamang ng HTML (isang talahanayan sa kasong ito). Dahil ang CakePHP ay nagbibigay ng isa pang paraan upang tukuyin ang isang template ng pahina, at ang view ay ipinasok sa template na iyon. Ang workbench ay nagbibigay din sa amin ng ilang mga bagay na katulong upang maisagawa karaniwang gawain sa panahon ng paglikha ng mga bahagi Mga pahina ng HTML(paglalagay ng mga form, link, Ajax o JavaScript).
I-save ang view bilang listahan.ctp(listahan ang pangalan ng aksyon at ang ibig sabihin ng ctp ay CakePHP template) sa folder /app/views/books(dahil ito ay isang view para sa isang controller action).
Ito ay kung paano isinasagawa ang lahat ng tatlong bahagi gamit ang workbench ng CakePHP!