On 11/07/2016 12:46 AM, teppo.p...@gmail.com wrote:
torstai 3. marraskuuta 2016 14.45.49 UTC Ethan Furman kirjoitti:
On 11/03/2016 01:50 AM, teppo wrote:

The guide is written in c++ in mind, yet the concepts stands for any
  programming language really. Read it through and think about it. If
  you come back to this topic and say: "yeah, but it's c++", then you
  haven't understood it.

The ideas (loose coupling, easy testing) are certainly applicable in
 Python -- the specific methods talked about in that paper, however,
 are not.

Please elaborate. Which ones explicitly?

The ones in place solely to make testing easier.

Others, such as passing an Engine into Car instead of making Car create its 
own, are valid.  To compare to what I said elsewhere, the exact Engine used is 
*not* an implementation detail -- any particular Car could have a range of 
Engines that work with it, and which is used is not determined by the car 
itself.

To go back to the original example:

def __init__(self, ...):
      self.queue = Queue()

we have several different (easy!) ways to do dependency injection:

* inject a mock Queue into the module
* make queue a default parameter

If it's just testing, go with the first option:

import the_module_to_test
the_module_to_test.Queue = MockQueue

and away you go.

This is doable, but how would you inject queue (if we use Queue as an
 example) with different variations, such as full, empty, half-full,
 half-empty. :) For different tests.

In this case I would go with my last example:

ex = Example()
test_queue = generate_half_empty_queue()
ex.queue = test_queue
# do the testing

If the class in question has legitimate, non-testing, reasons to specify 
different Queues, then make it a default argument instead:

def __init__(self, ..., queue=None):
      if queue is None:
          queue = Queue()
      self.queue = queue

I already stated that this is is fine, as long as the number of arguments
 stays in manageable levels.

How is having 15 arguments in a .create() method better than having 15 
arguments in __init__() ?

 Although I do think testing is good enough
 reason to have it injected anytime. For consistency, it makes sense to
 have same way to create all objects. I wouldn't suggested of using that
 mechanism in public API's, just in internal components.

And that consistent way is to just call the class -- not to call class.create().

or, if it's just for testing but you don't want to hassle injecting a
 MockQueue into the module itself:

def __init__(self, ..., _queue=None):
      if _queue is None:
          _queue = Queue()
      self.queue = _queue

or, if the queue is only initialized (and not used) during __init__
 (so you can replace it after construction with no worries):

class Example:
      def __init__(self, ...):
          self.queue = Queue()

ex = Example()
ex.queue = MockQueue()
# proceed with test

This I wouldn't recommend. It generates useless work when things start to 
change,
 especially in large code bases. Don't touch internal stuff of class in tests 
(or
 anywhere else).

So, if you use the create() method, and it sets up internal data structures, 
how do you test them?  In other words, if create() makes that queue then how do 
you test with a half-empty queue?

The thing each of those possibilities have in common is that the normal use-case
 of just creating the thing and moving on is the very simple:

my_obj = Example(...)

To sum up:  your concerns are valid, but using c++ (and many other language)
 idioms in Python does not make good Python code.

This is not necessarily simply a c++ idiom, but a factory method design pattern 
[...]

Not all design patterns make sense in every language.

--
~Ethan~
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to