0

In my program, I have a long-running function from which I would like to interact with the user for various reasons:

  1. Giving status updates ("Downloading file /foo/bar.png")
  2. Displaying warnings ("File upload failed; will retry later")
  3. Asking for input ("Please enter your google drive password")

The problem is, of course, that this function doesn't know how it should interact with the user. For example, it would be nonsensical to display messages on stdout in a GUI program.

In case of errors, the solution is obvious - just throw an exception that'll be caught in the user interface layer. Errors are easy because the service layer's job is done as soon as the exception is thrown. But what about things like warnings and input? How do I bridge the gap between the service layer and the user?

  • 1
    In this case you would use an interface to define the hooks that the service layer would use to decouple it from the UI. The UI implements that interface and handles the UI related part of the work. Since Python doesn't really have the concept of an interface, what we are really talking about is a base class that doesn't define implementations. – Berin Loritsch Nov 6 '18 at 23:22
  • @BerinLoritsch But then how would I use that interface/base class? Would I pass an instance of that as an argument to my function? Or would I make that instance a global variable? Or something else? – Aran-Fey Nov 6 '18 at 23:25
  • I would pass it as an argument. – Berin Loritsch Nov 6 '18 at 23:27
  • You need to implement some relevant patterns. Check MVVM, MVP, etc. I don't know whether there are out-of-the-box frameworks in Python though In general, your long-running function should have some property (eg. progressStatus) and then the GUI should monitor that propertyand change the GUI elements as required. In most of the patterns, you should not pass and instance of the running function back to the class responsible for the GUI – John Kouraklis Nov 7 '18 at 0:38
3

Well, what I usually do in situations like this (and my answer isn't specific to Python in this case), is to provide some mechanism within the user-interface layer that allows the user-client code to define "callbacks." The service-layer calls out to the user-interface layer at strategic intervals. This gives the UI layer the chance to invoke a callback should the user have defined one. (If he did not, the call is simply a no-op.)

The "separation of concerns" thus remains clear: the service-layer will call the UI-layer regardless, not knowing (or caring) whether the UI-layer has anything that it should call, and having no idea what such a call would be.

In the Python language, the "callbacks" would of course be closures.

  • There are 2 things I don't understand: 1) Who or what is the "user-client code"? 2) Why would the callbacks be closures? – Aran-Fey Nov 7 '18 at 6:45
  • 1
    @Aran-Fey The callbacks could also be grouped into an notification strategy object. Closure here just means that the callback or notification strategy can have access to UI state without having to pass this extra data as a parameter of each event. – amon Nov 7 '18 at 13:55
  • I use the term "user-client code" to refer to "the part of the end-user's program that is making calls to the user-interface layer," in this case to specify the callback that should be called. The service layer has no idea whether any such callback exists – it doesn't care. – Mike Robinson Nov 7 '18 at 15:47
  • Huh. I thought the UI layer is the end-user's program... – Aran-Fey Nov 7 '18 at 16:34
  • A callback would only need to be a closure if you wanted to set some value you knew when creating it, sometime before calling it. Otherwise any ol function shoved in a var works just fine as a callback. – candied_orange Nov 7 '18 at 23:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.