MVCã䜿çšããããšã¯ãããžãã¹ã¢ããªã±ãŒã·ã§ã³ãã²ãŒã ã管çè ã¿ã€ãã®ã€ã³ã¿ãŒãã§ãŒã¹ããŸãã¯Gmail / Googleããã¥ã¡ã³ãã®ãããªãã®ã«ããé©ããŠãããšäºåã«èšããŸãã 1000åã®ãããŒãšé£ã¶éªçãç»é¢ã«è¡šç€ºãããããã¢ãŒã·ã§ã³ãµã€ããããå ŽåãMVCã®äœ¿çšã¯ç¡æå³ã§æ害ã§ãããããŸãã
ã¡ãã£ãšããæŽå²ïŒ
MVCïŒModel View ControllerïŒã¯ã1979幎ã«SmallTalkèšèªã«ã€ããŠã説æãããŸããã
ãã以æ¥ãã€ã³ã¿ãŒãã§ãŒã¹ã§ã¯æ°ãããã®ã¯äœãçºæãããŠããŸããã ãã³ãã¬ãŒããæ¹åãããè£è¶³ãããæ¡åŒµãããŸããã ãã©ãããã©ãŒã ã®æ©èœãšHMVC ïŒéå±€ã¢ãã«ãã¥ãŒã³ã³ãããŒã©ãŒïŒã MVP ïŒã¢ãã«ãã¥ãŒãã¬ãŒã³ã¿ãŒïŒã MVVM ïŒã¢ãã«ãã¥ãŒViewModelïŒãªã©ãã¢ããªã±ãŒã·ã§ã³ã®èšè¿°èšèªã«å¿ããŠãã©ãã¯ãŒãç»å ŽããŸããããæ¬è³ªã¯å€ãããŸãã-ããŒã¿ãœãŒã¹ã®å€æŽãšã³ãã³ãããã®åé¢é¢æ°ãšãã®åŒ±ãçµåã
1979幎ã«ãã³ãã¬ãŒãã衚瀺ãããŠä»¥æ¥ãæåã®ã¬ãã¥ãŒã¯æ¬¡ã®ãããªãã®ã§ãã£ãããšãäºåã«èšããŸããã ãªããããå¿ èŠãªã®ã§ãããããããã§ãã¹ãŠãããŸãæ©èœããŸãã ãã2çªç®ã®ã¬ãã¥ãŒïŒ ããããŠæåŸã«ã3çªç®ïŒã ã¢ãã«ãå€æŽããããã³ã«ããã¥ãŒãæŽæ°ãããŸãããã¹ãŠãé ããªããŸãïŒ ã ãããã£ãŠããã®èšäºãèªããšãã«åæ§ã®è³ªåãããããšã«é©ããªãã§ãã ããã ãã¹ãŠã«ããããããMVCãšãã®ãã©ãã¯ãŒã¯ãããªãã®æ°ã®ã¿ã¹ã¯ã«ã€ããŠéåžžã«æåããå®çžŸã®ãããœãªã¥ãŒã·ã§ã³ã«ãªããŸããã ãããã芧ãã ããã
ã¿ã¹ã¯ ïŒå€ãè¿œå ããã³åé€ããããã®ãåçŽãªã€ã³ã¿ã©ã¯ãã£ãããŒãžïŒå€ã®ãªã¹ãã2ã€ã®ãã¿ã³ïŒãäœæããŸãã åŸæ¥ã®ã¢ãããŒãã¯éåžžã«åçŽã§ã-HTMLãã¡ã€ã«ãäœæããŸãã éžæã¯ãªã¹ãã«äœ¿çšããããã¿ã³ã¯æäœã«äœ¿çšãããŸãã ãã¿ã³ã®onClickå±æ§ã§ã¯ãJavaScripté¢æ°åŒã³åºããèŠå®ããŠããŸãã selectããå€ãåé€ããã«ã¯ãDOM selectãªããžã§ã¯ãã䜿çšããselectedIndexããããã£ãèŠæ±ããŠã察å¿ãããªããžã§ã¯ããåé€ããŸãã
<html><head><script> // <![CDATA[ function addItem (value) { if (!value) { return; } var myselect = document.getElementById('myselect'); var newoption = myselect.appendChild(document.createElement('option')); newoption.value = value; newoption.innerHTML=value; } function removeCurrentItem () { var myselect = document.getElementById('myselect'); if (myselect.selectedIndex === -1) { return; } var selectedOption = myselect.options[myselect.selectedIndex]; selectedOption.parentNode.removeChild(selectedOption); } // ]]> </script> </head> <body> <select id="myselect" size="4"></select> <button onClick="addItem(prompt('enter value'))">+</button> <button onClick="removeCurrentItem()" />-</button> </body> </html>
ãã¹ãŠãã·ã³ãã«ã§åé¡ãªãåäœããŸãã ãªãä»ã®äœããå¿ èŠãªã®ã§ããïŒ ãã®ã¢ããªã±ãŒã·ã§ã³ã®å Žåãåé¡ã¯äºæ³ãããŸããããçµ±èšã«ãããšãå®éã«ã³ãŒããèšè¿°ããæéã¯çŽ25ïŒ ã§ãæ®ãã®75ïŒ ã®ããã°ã©ããŒã¯éçºããããžã§ã¯ãã®ãµããŒããæ°ããæ©èœã®è¿œå ãè¡ã£ãŠããŸãã ãŸããã¢ããªã±ãŒã·ã§ã³ãããè€éã«ãªããæ°ãå¢ãããšãã³ãŒãã®ç¶æããŸããŸãé£ãããªãããã°ã®æ°ãå¢ããŸãã
ãJquery / Dojo / MooTools / My_Love_Ajax_Libraryã䜿çšããŠå®è¡ããŸãããšããªãã¯èšããŸãã ããšãã°ã次ã®ããã«ïŒ
<html><head> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script> <script> // <![CDATA[ $(document).ready(function(){ $('#plus').bind('click', function(){ var value = prompt('add value'); if (!value) { return; } $('<option>').html(value).appendTo($('#myselect')); }); $('#minus').bind('click', function(){ var myselect = $('#myselect'); if (myselect.attr('selectedIndex') === -1) { return; } myselect.children().remove(':selected'); }); }); // ]]> </script> </head> <body> <select id="myselect" size="4"></select> <button id="plus">+</button> <button id="minus"/>-</button> </body> </html>
ããã¯ããã«è¯ããªããã³ãŒãã¯ããã³ã³ãã¯ãã«ãªãããšãŠãã¯ãŒã«ã«èŠããŸãã ãã ããããã¯äž»ãªåé¡ã解決ããŸããã§ãããããŒã¿ïŒãªã¹ãïŒã¯ãŠãŒã¶ãŒã€ã³ã¿ãŒãã§ã€ã¹å ãselectèŠçŽ ã®htmlå ã«ä¿åãããŸãã 2çªç®ã®æ¬ ç¹ã¯ãhtmlããã³javascriptã³ãŒãã®ããã·ã¥ãããããšã§ãã ãã®ã·ã³ãã«ãã«ãããããããéçºã¯äžååã§ãã ããšãã°ãäžåžãç§ãã¡ã®ãšããã«æ¥ãŠãããªã¹ãã§äœãã匷調衚瀺ãããŠããå Žåã«ã®ã¿ãã€ãã¹ãã¿ã³ã衚瀺ãããããšããæãåçŽãªèŠæ±ãèšããŸãã ããã«ãããã°ã©ããŒãšããŠãåé¡ãçºçããŸãããã®å€æŽãè¡ãå Žæ-HTMLãŸãã¯JavaScriptã ãããŒããhtmlã§ãã¿ã³ããªãã«ããŸãããšããªãã¯èšããŸãã ãŸããäŒæäžã«ååã¯JavaScriptå ã§åæ§ã®æäœãè¡ããŸãã
äžèŠåçŽãªå€æŽãã³ãŒããéåžžã«æ··ä¹±ããå§ããããšãå€æããããããããé¿ããããšæããŸãã
åŸæ¥ã®MVCå®è£ ã§ã¯ãåºæ¬çãªååã¯æ¬¡ã®ãšããã§ãã
- 匱ãçµå
- ã¢ãã«ã¯èª°ã«ã€ããŠãäœãç¥ããŸããã ã¢ãã«ã¯ãå¿ èŠã«å¿ããŠãããšãã°ãViewããªãã¹ã³ã§ããã¢ã©ãŒããéä¿¡ããŸãã
- ãã¥ãŒã¯ã¢ãã«ã«ã€ããŠã¯ç¥ã£ãŠããŸãããå€æŽããããšã¯ã§ããŸããããã¥ãŒã¯ã³ã³ãããŒã©ãŒãæäœã§ããŸã
- ã³ã³ãããŒã©ãŒã¯ã¢ãã«ã«ã€ããŠç¥ã£ãŠããããããå€æŽã§ããŸãããŸãããã¥ãŒã«ã€ããŠãç¥ã£ãŠããããããå€æŽã§ããŸãïŒãããïŒã
ãã®å Žåãéãã¯æ¬¡ã®ãšããã§ãã
- ã¢ãã«ã¯èª°ã«ã€ããŠãäœãç¥ããŸããã ã¢ã©ãŒããéä¿¡ã§ããŸã
- ãã¥ãŒã¯ã¢ãã«ã«ã€ããŠã®ã¿ç¥ã£ãŠãã
- ã³ã³ãããŒã©ãŒã¯ããã¥ãŒãšã¢ãã«ã«ã€ããŠèªèããŠããŸãã
ã芧ã®ãšãããåŸæ¥ã®ãã³ãã¬ãŒããšã¯ç°ãªãããã®ãã³ãã¬ãŒãã¯ããå³æ Œã§ãã ãã¥ãŒã¯ã³ã³ãããŒã©ãŒã«ã€ããŠäœãç¥ããªãã®ã§ããªããžã§ã¯ãïŒViewãšControllerïŒãã¯ãã¹ãªã³ã¯ãããšããèãã¯å¥œãã§ã¯ãããŸãããä»ã®ãšããããã®ãããªãªã³ã¯ãªãã§ããããšããããšãã§ããŸãã ããšãã°ãOrthodox MVCïŒOrthodox MVCïŒãšåŒã³ãŸããã
ã¢ãã«
ãã®å Žåã®ã¢ãã«ã¯ãå ¥åãããªããžã§ã¯ãïŒæååïŒã®é åãåã«æ ŒçŽããŸãã ãçŸåšã®ããªããžã§ã¯ãã®ã·ãªã¢ã«çªå·ãä¿åãããŸãã ãªããžã§ã¯ããè¿œå ããã³åé€ããããã®æšæºã¡ãœããããããŸãïŒaddItemãremoveCurrentItemãããããªãã§ã¯å®è¡ã§ããŸããã ã¢ãã«ã«ã¯ããã®å€æŽãéç¥ããå¿ èŠããããŸãã
ã¢ã©ãŒããéä¿¡ããã«ã¯ããµããžã§ã¯ãã¿ã€ãã®ãªããžã§ã¯ãïŒmodelChangedSubjectïŒã䜿çšããŸãããããã®ãªããžã§ã¯ããããã€ãäœæãããã¢ãã«ãå€æŽãããŸãããã ãã§ãªãããã¢ãã«Xããããã£ãå€æŽãããŸãããéç¥ããµãã¹ã¯ã©ã€ããŒã«éä¿¡ã§ããŸãã ããã«ããããã¥ãŒãå®å šã«æŽæ°ãããã®ã§ã¯ãªããã¢ãã«å ã§å®éã«å€æŽãããé åã®ã¿ãæŽæ°ãããŸãã
jqueryã§ãµããžã§ã¯ããäœæããããã®æšæºé¢æ°ãèŠã€ãããªãã£ããããå®æããmakeObservableSubjectãã«ããã®ããã°ã©ããŒã«ããåªããmvcèšäºããåããŸããã
... OMVC.Model = function () { var that = this; var items = []; this.modelChangedSubject = OMVC.makeObservableSubject(); this.addItem = function (value) { if (!value) { return; } items.push(value); that.modelChangedSubject.notifyObservers(); }; this.removeCurrentItem = function () { if (that.selectedIndex === -1) { return; } items.splice(that.selectedIndex, 1); that.modelChangedSubject.notifyObservers(); }; this.getItems = function () { return items; }; this.selectedIndex = -1; this.getSelectedIndex = function () { return that.selectedIndex; } this.setSelectedIndex = function (value) { that.selectedIndex = value; that.modelChangedSubject.notifyObservers(); } }; ...
衚瀺ãã
ã芧ã®ãšãããhtmlã«ã¯ã空çœãã®æ¬æãããããã¹ãŠã®èŠçŽ ã¯ãæåãã§æç»ãããŸãã JavaScriptã䜿çšããŸãã GmailãèŠãŠãã ãã-ãããåãã§ãã ãã¹ãŠã®èŠçŽ ïŒãã¿ã³ããªã¹ãã¯javascriptã§æç»ãããŸãã
ãã¥ãŒã¯ã¢ãã«ã«ã€ããŠç¥ã£ãŠããŸãããçæ³çã«ã¯ããã¥ãŒã¯èªã¿åãå°çšã¢ãŒãã§ã¢ãã«ã«ã¢ã¯ã»ã¹ã§ããå¿ èŠããããŸãã æ¬åœã«ãããå Žåã§ããå¿ãremoveCurrentItemãåŒã³åºãããšãèš±å¯ããããšã¯ã§ããŸããã
æ®å¿µãªãããJavaScriptã§ãããé¢æ°ãå¥ã®ãªããžã§ã¯ããžã®å€æŽãèš±å¯ããä»ã®é¢æ°ã«èªã¿åãå°çšã¢ã¯ã»ã¹ã®ã¿ãèš±å¯ããæ¹æ³ããŸã ç解ããŠããŸããã ãããéæããæ¹æ³ã«ã€ããŠã¢ã€ãã¢ãããã°ãå ±æããŠãã ããã
... OMVC.View = function (model, rootObject) { var that = this; that.select = $('<select/>').appendTo(rootObject); that.select.attr('size', '4'); that.buttonAdd = $('<button>+</button>').appendTo(rootObject).height(20); that.buttonRemove = $('<button>-</button>').appendTo(rootObject).height(20); model.modelChangedSubject.addObserver(function () { var items = model.getItems(); var innerHTML = ''; for (var i = 0; i<items.length; i += 1) { innerHTML += "<option>"+items[i]+"</option>"; } that.select.html(innerHTML); }); }; ...
ã³ã³ãããŒã©ãŒ
ãã®ã³ã³ãããŒã©ãŒã§ã¯ãã¢ãã«ãšãã¥ãŒããªã³ã¯ããã ãã§ããã¥ãŒãªããžã§ã¯ãã«ã€ãã³ããæããã ãã§ãã
... OMVC.Controller = function (model, view) { view.buttonAdd.bind('click', function () { model.addItem(prompt('addvalue')); }); view.buttonRemove.bind('click', function () { model.removeCurrentItem(); }); view.select.bind('click', function () { model.setSelectedIndex(view.select[0].selectedIndex); }); }; ...
å šæããã¹ãïŒ
<!doctype html> <html> <body> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script> <script> // <![CDATA[ var OMVC = {}; OMVC.makeObservableSubject = function () { var observers = []; var addObserver = function (o) { if (typeof o !== 'function') { throw new Error('observer must be a function'); } for (var i = 0, ilen = observers.length; i < ilen; i += 1) { var observer = observers[i]; if (observer === o) { throw new Error('observer already in the list'); } } observers.push(o); }; var removeObserver = function (o) { for (var i = 0, ilen = observers.length; i < ilen; i += 1) { var observer = observers[i]; if (observer === o) { observers.splice(i, 1); return; } } throw new Error('could not find observer in list of observers'); }; var notifyObservers = function (data) { // Make a copy of observer list in case the list // is mutated during the notifications. var observersSnapshot = observers.slice(0); for (var i = 0, ilen = observersSnapshot.length; i < ilen; i += 1) { observersSnapshot[i](data); } }; return { addObserver: addObserver, removeObserver: removeObserver, notifyObservers: notifyObservers, notify: notifyObservers }; }; OMVC.Model = function () { var that = this; var items = []; this.modelChangedSubject = OMVC.makeObservableSubject(); this.addItem = function (value) { if (!value) { return; } items.push(value); that.modelChangedSubject.notifyObservers(); }; this.removeCurrentItem = function () { if (that.selectedIndex === -1) { return; } items.splice(that.selectedIndex, 1); that.modelChangedSubject.notifyObservers(); }; this.getItems = function () { return items; }; this.selectedIndex = -1; this.getSelectedIndex = function () { return that.selectedIndex; } this.setSelectedIndex = function (value) { that.selectedIndex = value; that.modelChangedSubject.notifyObservers(); } }; OMVC.View = function (model, rootObject) { var that = this; that.select = $('<select/>').appendTo(rootObject); that.select.attr('size', '4'); that.buttonAdd = $('<button>+</button>').appendTo(rootObject).height(20); that.buttonRemove = $('<button>-</button>').appendTo(rootObject).height(20); model.modelChangedSubject.addObserver(function () { var items = model.getItems(); var innerHTML = ''; for (var i = 0; i<items.length; i += 1) { innerHTML += "<option>"+items[i]+"</option>"; } that.select.html(innerHTML); }); }; OMVC.Controller = function (model, view) { view.buttonAdd.bind('click', function () { model.addItem(prompt('addvalue')); }); view.buttonRemove.bind('click', function () { model.removeCurrentItem(); }); view.select.bind('click', function () { model.setSelectedIndex(view.select[0].selectedIndex); }); }; $(document).ready(function () { var model = new OMVC.Model(); var view = new OMVC.View(model, $('<div/>').appendTo($("body"))); var controller = new OMVC.Controller(model, view); }); // ]]> </script> </body> </html>
ãããã«
æåŸã«ã åé¡ã¯ ãMVCå®è£ ãæåã®æšæºãŸãã¯æ¹è¯ãããjQuery-evãããåªããŠããç¹ã¯äœã§ããïŒ ç§ãã¡ã¯3åã®ã³ãŒããæžããŸããããäœã®ããã§ããïŒ
åç ïŒãã®ãããªåçŽãªäŸã§ããMVCãã³ãã¬ãŒãã®å©ç¹ã¯åŸã ã«é¡èã«ãªãå§ããŠããŸãã
- ãã¹ãŠã®èŠçŽ ãžã®ãªã³ã¯ã¯ãã§ã«ãã¥ãŒã«ä¿åãããŠãããããåŸã§document.getElementByIdããã³åæ§ã®é¢æ°ã«ãã£ãŠããããåä¿¡ããããã«idèŠçŽ ãã€ã³ã¿ãŒãã§ã€ã¹ã«å²ãåœãŠãå¿ èŠã¯ãããŸãããhtmlããã¥ã¡ã³ãã§ããããæ€çŽ¢ããå¿ èŠã¯ãããŸããã
- ãã¹ãŠã®ãªããžã§ã¯ãã¯rootObjectãšã¬ã¡ã³ãå ã«æç»ããããã¥ãŒã¯rootObjectã«å¯ŸããŠäœåºŠã§ãç°¡åã«äœ¿çšã§ããŸãã
- htmlæ¬æã«ã¯äœãä¿åãããŸããã ãã¡ãããããã«ã¯äœãããããŸãããããã«ã¯ãŸã£ããèå³ããããŸãããå€æŽåã¯ãhtmlãšJavaScripté¢æ°ã®2ã€ã®å Žæãæ¢ããŠå®è¡ããå¿ èŠããããŸããã ããã§ãã¹ãŠãJavaScriptã§ã®ã¿è¡ãããããã«ãã¶ã€ã³ã¯Viewãªããžã§ã¯ãã«ã®ã¿ä¿åãããŸãã
- å€æŽã¯éåžžã«ç°¡åã§ãã ããšãã°ãäžèšã®ããã¹ããªã¯ãšã¹ãã¯æ¬¡ã®ããã«å®è£
ãããŸãã
- selectedIndexãå€æŽãããµããžã§ã¯ããäœæããŸã
this.selectedIndexChangedSubject = OMVC.makeObservableSubjectïŒïŒ; - å€æŽããå Žåã¯ããã®ããšãéç¥ããŸããselectedIndexChangedSubject.notifyObserversïŒïŒ;
- ãããŠããã©ãŒã ã§selectedIndexChangedSubjectã¢ã©ãŒãããµãã¹ã¯ã©ã€ãããŸã
- selectedIndexãå€æŽãããµããžã§ã¯ããäœæããŸã
ããã¯ã©ã€ãä»ãã®æ©èœãè¿œå ããå®å šãªæçµããŒãžã§ã³ïŒ
<!doctype html> <html> <body> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script> <script> // <![CDATA[ var OMVC = {}; OMVC.makeObservableSubject = function () { var observers = []; var addObserver = function (o) { if (typeof o !== 'function') { throw new Error('observer must be a function'); } for (var i = 0, ilen = observers.length; i < ilen; i += 1) { var observer = observers[i]; if (observer === o) { throw new Error('observer already in the list'); } } observers.push(o); }; var removeObserver = function (o) { for (var i = 0, ilen = observers.length; i < ilen; i += 1) { var observer = observers[i]; if (observer === o) { observers.splice(i, 1); return; } } throw new Error('could not find observer in list of observers'); }; var notifyObservers = function (data) { // Make a copy of observer list in case the list // is mutated during the notifications. var observersSnapshot = observers.slice(0); for (var i = 0, ilen = observersSnapshot.length; i < ilen; i += 1) { observersSnapshot[i](data); } }; return { addObserver: addObserver, removeObserver: removeObserver, notifyObservers: notifyObservers, notify: notifyObservers }; }; OMVC.Model = function () { var that = this; var items = []; this.modelChangedSubject = OMVC.makeObservableSubject(); this.addItem = function (value) { if (!value) { return; } items.push(value); that.modelChangedSubject.notifyObservers(); }; this.removeCurrentItem = function () { if (that.selectedIndex === -1) { return; } items.splice(that.selectedIndex, 1); if (items.length === 0) { that.setSelectedIndex(-1); } that.modelChangedSubject.notifyObservers(); }; this.getItems = function () { return items; }; this.selectedIndex = -1; this.getSelectedIndex = function () { return that.selectedIndex; } this.selectedIndexChangedSubject = OMVC.makeObservableSubject(); this.setSelectedIndex = function (value) { that.selectedIndex = value; that.selectedIndexChangedSubject.notifyObservers(); } }; OMVC.View = function (model, rootObject) { var that = this; that.select = $('<select/>').appendTo(rootObject); that.select.attr('size', '4'); that.buttonAdd = $('<button>+</button>').appendTo(rootObject).height(20); that.buttonRemove = $('<button>-</button>').appendTo(rootObject).height(20).fadeOut(); model.modelChangedSubject.addObserver(function () { var items = model.getItems(); var innerHTML = ''; for (var i = 0; i<items.length; i += 1) { innerHTML += "<option>"+items[i]+"</option>"; } that.select.html(innerHTML); }); model.selectedIndexChangedSubject.addObserver(function () { if(model.getSelectedIndex() === -1) { that.buttonRemove.fadeOut(); } else { that.buttonRemove.fadeIn(); } }); }; OMVC.Controller = function (model, view) { view.buttonAdd.bind('click', function () { model.addItem(prompt('addvalue')); }); view.buttonRemove.bind('click', function () { model.removeCurrentItem(); }); view.select.bind('click', function () { model.setSelectedIndex(view.select[0].selectedIndex); }); }; $(document).ready(function () { var model = new OMVC.Model(); var view = new OMVC.View(model, $('<div/>').appendTo($("body"))); var controller = new OMVC.Controller(model, view); }); // ]]> </script> </body> </html>
以åã¯ã€ã³ã¿ã©ã¯ãã£ããªHTMLããŒãžããããŸããããä»ã§ã¯HTMLã®ã°ã©ãã£ã«ã«ã·ã§ã«ãåããjavascriptã¢ããªã±ãŒã·ã§ã³ããããŸãã éããæããŠãã ããã