2

I'm currently new to Python and am learning about the MVC architectural pattern. For simplicity sake and ease of explaining, let's say I have 3 classes. (Model, View, & Controller).

class Controller  
  def __init__(self):
    self.view = View()
    self.model = Model()

  def some_function(self):
    return self.model.add_things(1,2)

class Model
  def __init__(self):
    pass

  def add_things(self, x, y):
    return x + y

class View:
  def __init__(self):
    pass

Is it normal practice to instantiate my views and models as instance variables for the Controller as above?

Or is it better to import the files manually (i.e. import Model) at the top of Controller file and call the methods directly in the controller?

from Model import Model 
from View import View

class Controller  
  def __init__(self):
     pass

 def some_function(self):
     return Model.add_things(1, 2)

class Model
  def __init__(self):
    pass

  def add_things(self, x, y):
    return x + y

class View:
  def __init__(self):
    pass
yesyoukenn
  • 207
  • 4
  • 17
  • 1
    Additionally, I'm unclear as the the differences between the two, if any, and when one design would be favored over the other? – yesyoukenn Feb 18 '15 at 04:55

1 Answers1

1

I think your example isn't quite up to snuff. In your second code snippet, you'd still (presumably) be dealing with classes (Model and View), so this code seems a bit strange to me:

from Model import Model 
from View import View

class Controller  

    def some_function(self):
        # How are you calling the method on the class and not the instance?
        return Model.add_things(1, 2)

You'd have to do something like return Model().add_things(1, 2) to get an instance from which to call the add_things method (unless the function was decorated with classmethod). However, that wouldn't really make any sense and I'll venture you probably didn't mean that. What you actually seem to be asking for is the difference between initializing an object in the constructor vs using a globally declared instance, so something like this:

# It seems that for your question, it shouldn't really matter whether the 
# class `Model` is imported or declared inside the same module. So it could 
# be either way.
# 1) class Model(): pass
# 2) from Model import Model    

# This would initialize `Model` at the module-level
modelobject = Model()

class Controller  

    def some_function(self):
        # And then it would be possible to use it as you described.
        return modelobject.add_things(1, 2)

Back to the question...

To answer you I first had to make you realize what you were really asking. The point is there isn't anything wrong or remotely controversial with importing your Model or View from outside vs defining them within the same module they're being used in, so asking about that doesn't really make much sense; the truth is that would really depend on your program structure and, more generally, how big your project is. The bigger and more complex, the more unlikely these classes or collection of classes would intermingle in the same module. They'd get separated into different modules pretty quickly.

So your real question as far as I've understood it is whether or not it's best to initialize a Model or View object inside the constructor of your Controller or to use them as global instances you can call from your Controller class. Quite frankly, I think what you're really asking about is something akin to a global instance pattern (for example, something like a Singleton class).

You haven't specifically made use of the term Singleton, but asking for a way to use a globally-declared instance of a class smells very much like this is what you're asking about.

You can read about its pitfalls here, here or here.

A better approach is Dependency Injection.

To reference your original example, it may--with objects lower down in your code hierarchy--be preferable to use the following method (to obtain a single instance throughout your program):

class SubController
    def __init__(self, modelobject, viewobject):
        self.view = viewobject
        self.model = modelobject

In other words, it's not unlike a singleton in that you'd essentially be making use of a single instance, if this is indeed what you need. However, it works not by declaring a global instance (which would introduce coupling and many other problems discussed in the linked articles), but by letting the class acquire its dependencies via the calling context (which has considerable advantages). So you pass around the Model and View objects directly via constructor methods as opposed to instantiating them directly inside classes (as in your example) or obtaining them with a globally declared variable.

Of course, you'd always have the main class or function where you'd instantiate these objects and pass them around from there on out. This approach has the benefit of being more flexible later down the road!

Community
  • 1
  • 1
Eithos
  • 2,421
  • 13
  • 13
  • Thanks so much! What about in the case of a database connection. Let's pretend I'm not using an ORM and am instead wrapping sqlite3 commands into a class. Would Dependency Injection be appropriate here? If so, should I initialize in the constructor of the Controller? – yesyoukenn Feb 18 '15 at 21:02
  • Dependency injection is a design pattern that promotes loose coupling, so it's not so much about _where_ you choose to initialize objects; the idea is you initialize them once (ideally at the highest level of your program) so you can pass them around to other objects that might need them. In the case of a wrapper class, if it's designed to wrap around one specific set of commands or class, it may indeed make more sense to initialize your wrapped objects inside the constructor of the Controller. – Eithos Feb 19 '15 at 00:49
  • And if you're just wrapping module-level functions from another module, you would simply import the module (as you typically would, not inside the class obviously) and call those functions normally. The point is that you have to determine whether it matters or not if an object knows _too much_ about the object/s it's using and passing around. If, for example, you decide in the future that you'd like to swap the `View`, you'd be able to achieve this easily using dependency injection (assuming the other `View` conforms to the same or similar interface). – Eithos Feb 19 '15 at 00:50