I have a class that looks something like the following:
# Class violates the Single Responsibility Principle
class Baz:
data = [42]
def do_foo_to_data(self):
# call a dozen functions that do complicated stuff to data
def do_bar_to_data(self):
# call other functions that do different stuff to data
I want to break it into two separate classes because it violates the SRP. The functions called by do_foo_to_data()
are completely distinct from those called by do_bar_to_data()
. Yet they must operate on the same data
.
I've come up with a bunch of solutions, but they're all ugly. Is there a way to do this cleanly, preferably in Python 3 (though 2.7 is OK too)?
The best of my "solutions" is below:
# I find this hard to read and understand
class Baz:
data = [42]
def create_foo(self):
return Baz.Foo()
def create_bar(self):
return Baz.Bar()
class Foo:
def do_foo_to_data(self):
# call foo functions
class Bar:
def do_bar_to_data(self):
# call bar functions
Note: It's not essential to me that the data
member be a class member.
I only expect to create one instance of Baz
; but I didn't want to ask two questions in one post and start a discussion about singletons.
This is not an elegant solution. You better pass a reference to the object you want them to operate on. So something like:
class Foo:
def __init__(self,data):
self.data = data
def do_foo_to_data(self):
#...
self.data[0] = 14
pass
class Bar:
def __init__(self,data):
self.data = data
def do_bar_to_data(self):
#...
self.data.append(15)
pass
(I added sample manipulations like self.data[0] = 14
and self.data.append(15)
)
And now you construct the data. For instance:
data = [42]
Next you construct a Foo
and a Bar
and pass a reference to data
like:
foo = Foo(data)
bar = Bar(data)
__init__
is what most programming languages call the constructor and as you have seen in the first fragment, it requires an additional parameter data
(in this case it is a reference to our constructed data
).
and then you can for instance call:
foo.do_foo_to_data()
which will set data
to [14]
and
bar.do_bar_to_data()
which will result in data
being equal to [14,15]
.
Mind that you cannot state self.data = ['a','new','list']
or something equivalent in do_foo_to_data
or do_bar_to_data
because this would change the reference to a new object. Instead you could for instance .clear()
the list, and append new elements to it like:
def do_foo_to_data(self): #alternative version
#...
self.data.clear()
self.data.append('a')
self.data.append('new')
self.data.append('list')
Finally to answer your remark:
preferably in Python 3 (though 2.7 is OK too)?
The technique demonstrated is almost universal (meaning it is available in nearly every programming language). So this will work in both python-3.x and python-2.7.