On 29/05/16 05:33, boB Stepp wrote: > I am currently mulling over some information Alan passed along a while > back on using the model-view-controller design pattern (Would > 'architectural pattern' be a better choice of phrase?).
Either is appropriate. Design Pattern is more usual, largely because it ws one of the first OOP patterns to be formally documented (in the SmalltTalk world) and is included in the original Design Patterns book by the GoF. > My current understanding is that the model contains program logic and > the data which the program logic manipulates. Correct, all the core entities which make up the program are represented by models. Most things stored in a database will have a corresponding model. > The view is just the interface with which the user interacts. Specifically the visible part of the interface. the "presentation layer". > The controller ties things together, refreshing the view appropriately, > receiving messages from the view when something there has > been changed by the user, interrogating the model when needed, > and receiving the model's results and refreshing the view > appropriately. The controller is where the confusion starts. Every implementation of the MVC pattern seems to change the way the controller is defined. You are broadly correct but some frameworks have a more view focussed interpretation, others more model focused. For example some GUI frameworks combine the view and controller concepts into a single Window object (the Delphi/VB/MFC/OWL Windows GUI frameworks all take that approach). The JSP type 1 framework is similar in that the JSP page does both roles. But in the JSP type 2 framework the controller is separated out into a distinct entity. What everybody(?!) seems to agree on is that events generated by the view are forwarded to a controller(*) which determines whether a model needs to be involved and is so what method of which model needs to be invoked. (*)If the controller is embedded in the view object then that is probably handled by an internal superclass method of the framework and based on an event table (similar to Tkinter's binding mechanism) The model responds, but how the view is notified varies. In some cases the views register with models and the model automatically sends notification messages to all registered views (by-passing the controller). In other cases the model si8mply sends a reply to the controller that invoked it and the controller decides which views should be updated. Thee is a modern twist on the whole thing too, where a View-Model is introduced. This is a model object that represents an aggregated view of lower level models and is closely aligned to the fields on a view. (Think of it being like a view table in a SQL database, an aggregation of multiple tables via a SELECT statement) In this case the controller may simply update the view-model and the view model will notify its associated view(s) directly. This is most commonly seen in the Web/Javascript world where the browser holds a View-Model in memory which is updated based on JSON queries back to the server which hosts the real models. This architecture allows rich web clients to do things like sorting or filtering the data without going back to the server. Another common use of a view-model is for collections. You may have many foo objects (each one a model in its own right) but a list view of them only wants a single model to work with so you create a view-model representing the collection of foo. Each foo is persisted in the database but the collection is a more abstract entity being in effect the table as a whole. > In my simple code to follow, I have the contents of three files, which > I have named model.py, controller.py and view.py. controller.py > starts program execution. My intent is to keep each of the three as > independent of the others as I can manage, so that ideally, I could > modify one of them and not need (or minimally need) to modify the > other files. In practice it's hard to separate the controller and view entirely (which is why some frameworks combine them) but it should definitely be possible to separate the models from the more UI elements. What the controller does do is allow you to swap different views within a single UI. For example a list view, versus a form view versus a graphical view, all of the same object or set of objects. > controller.py: > def main(): > '''Start and run the program.''' > > option_num = view.main_menu() > while True: > if option_num == '1': > diameter = float(view.diameter_prompt()) > circumf = model.circumference(diameter) > view.display_circumference(str(diameter), str(circumf)) > option_num = view.main_menu() > > elif option_num == '2': > view.display_exit_msg() > sys.exit() > > else: > title = 'WARNING!\n' > msg = ('That is not a valid option number.\nPlease enter a valid > ' + > 'number and press <Enter>.') > view.display_msg(title, msg) > option_num = view.main_menu() > > if __name__ == '__main__': > main() > ------------------------------------------------------------------------------------ > > model.py: > > #!/usr/bin/env python3 > > '''Simple 'model' program to explore the model-view-controller design pattern. > ''' > > import math > > PI = math.pi > > def circumference(diameter): > '''Calculate the circumference of a circle given its diameter.''' > > return PI * diameter > ------------------------------------------------------------------------------------ > > view.py: > > #!/usr/bin/env python3 > > '''Simple view program to explore the model-view-controller design pattern.''' > > def main_menu(): > '''Display main menu of options and return entered option number.''' > > print(''' > Option 1: Calculate the circumference of a circle. > Option 2: Exit this program. > > ''') > main_menu_option = input('Enter an option number: ') > return main_menu_option > > def diameter_prompt(): > '''Display a prompt to enter a diameter and return that value.''' > > print() > diameter = input('Enter a diameter: ') > return diameter > > def display_circumference(diameter, circumference): > '''Display the circumference calculated from the user-entered diameter.''' > > print() > print('You entered a diameter of', diameter, '.') > print('The circumference corresponding to that diameter is', > circumference, '.') > > def display_exit_msg(): > '''Display exiting the program message.''' > > print() > print('Exiting this program...\n') > > def display_msg(title, msg): > '''Display the passed title and message.''' > > print() > print(title) > print(msg) > ------------------------------------------------------------------------------------ > > Does my code capture the essence of MVC? For a CLI yes. But I'd probably not make the controller the main() function. Instead I'd have a separate main that constructed the objects. The controller then has a handle_event() type method that receives the event from the UI view. So you make the view responsible for initiating the event processing not the controller. The controller then becomes a fairly simple validate-dispatch mechanism. > anything or missing any nuances? Would this code scale upward as I > would hope? It would be a bit messy but could be adapted quite easily. For example instead of the if/elif chain use a dictionary for event dispatch. > That is, if I chose to add more options as to what could > be done with the program, would I only need to add only the code for > the new functionality and not have to rewrite any existing code? If I > were to change from CLI to GUI, would this similarly mean only a > rewrite of view.py without having to alter the other two files? No because UI are event driven and have their own event loop. That's why I'd move the main out of the controller. It's easier to have the UI call your controller and just relay the event info. I gotta go so I'll get back to the other questions later if nobody else has chimed in by then. -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.amazon.com/author/alan_gauld Follow my photo-blog on Flickr at: http://www.flickr.com/photos/alangauldphotos _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor