0

I've got into some interesting design question.

I have some abstract class, Let's Name it ServiceClass

Let's say I have 2 subclasses, Named CoolServiceClass and AmazingServiceClass, both inheriting from ServiceClass.

class ServiceClass(ABC):
    @abstractmethod
    def do_something(self):
        pass


class CoolServiceClass(ServiceClass):
    def do_something(self):
        return 'Cool!'

    def do_nothing(self):
        return 'Nothing!'


class AmazingServiceClass(ServiceClass):
    def do_something(self):
        return 'Amazing!'

    def just_do_it(self):
        return 'Nope'

I want to write the client code but I don't want it to be coupled to one of the sub-classes in the client code. I don't want the client code to be able to use any of the methods just_do_it or do_nothing, so I'll be able to replace between their implementations easily. I know that python is duck-typed language but I still wonder what is the proper way to handle this situation?

One solution I thought of is using some kind of proxy class that looks something like this:

class ProxyServiceClass(ServiceClass):
    def __init__(self, service_class):
        self.service_class = service_class

    def do_something(self):
        return self.service_class.do_something()

And then the client code will look like this:

service_class1 = ProxyServiceClass(CoolServiceClass())
service_class2 = ProxyServiceClass(AmazingServiceClass())

Now, if I'll try to use any of the methods just_do_it or do_nothing I'll get an exception.

I am a little bit skeptic about this solution because I haven't seen similar solutions, I'll be happy to hear your opinions.

Thanks!

JackPot16
  • 97
  • 1
  • 1
  • 10
  • due to your description the client code is supposed to dynamically call combined functionality from all subclasses - you need some kind of composition – RomanPerekhrest Aug 13 '19 at 18:16
  • So you intend to make `do_nothing` "private"? There really is nothing like that in python, everything is always accessible. Even with your solution, the user can use `service_class1.service_class.do_nothing()` – Ron Serruya Aug 13 '19 at 18:16
  • @RonSerruya, What I want is that only the methods that are defined in the abstract class will be available at the client, at least that the usage of the subclass methods won't be "obvious". – JackPot16 Aug 13 '19 at 18:25
  • 1
    In a statically typed language, you would simply require an instance of `ServiceClass`. Though said instance might actually be an instance of `AmazingServiceClass` and have access to `just_do_it`, the *client* cannot assume that and therefore cannot use it. In Python, the way to do this is to simply document that any particular subclass cannot be counted on. That is, the client should only use the interface made available by `ServiceClass`. – chepner Aug 14 '19 at 18:08

1 Answers1

0

If do_nothing is a private method (i.e. it must not be accessed from outside the class), rename it to __do_nothing (Why are Python's 'private' methods not actually private?)

If you want it to be accessible for some outside users, but not the others, I don't see another solution other than what you've described with a wrapper.

Nickolay
  • 31,095
  • 13
  • 107
  • 185