2

I have a lot of classes that are just a CRUD interface for microservices. They only have a param for the endpoint and some of the methods get_list / get_item / create / update / delete / activate / deactivate. To be DRY, the methods that are necessary for the microservice calls are all grouped in a BaseAPI class. A small example would be like that:

class GetItem(BaseAPI):
    API_ENDPOINT = 'www.example.com'
    
    @classmethod
    def get_list(cls):
        return cls.get(cls.path(cls.API_ENDPOINT))

    @classmethod
    def create(cls, data):
        return cls.post(cls.path(cls.API_ENDPOINT,data))

    ...

What I see here is that I just created an additional layer, and these methods, might as well live inside the BaseAPI; and then I would only have to add the endpoint. On the other hand, not all microservices support all the methods and I wouldn't want the classes to have methods that are not supported.

One way to solve it could be to pretend that the methods are private by adding an underscore and then just extend the ones that are supported. But this doesn't really solve the DRY part since I still have to write a new method for every supported method.

Another solution I thought of is to have one class for every method and use multiple inheritance to define which methods are supported.

class BaseGetList():
    @classmethod
    def get_list(cls):
        return cls.get(cls.path(cls.API_ENDPOINT))

class BaseCreate():
    @classmethod
    def create(cls, data):
        return cls.post(cls.path(cls.API_ENDPOINT,data))

class GetItem(BaseAPI,
              BaseGetList,
              BaseCreate):
    API_ENDPOINT = 'www.example.com'

I am being tempted to go towards the second solution but I am a bit concerned: is it easily understandable, developer friendly, and acceptable as a practice? Are there caveats that I don't see? Or is there any better way to solve this problem?

6
  • 3
  • At first sight, the second approach looks like mixins. From my point of view, not the most intuitive way, but it is certainly an acceptable practice.
    – Christophe
    Apr 6, 2021 at 13:15
  • @Christophe, indeed, mixin is the word.
    – xpy
    Apr 6, 2021 at 13:16
  • @xpy, I see cls being repeated quite often in your sample code. Do you consider that fine, or a violation or DRY? And what about that prefix Base to your class names? And I also see a chance for repetition in the list of base-classes/mixins for the GetItem-like classes. Do you see where I am going with this comment? Not all repetition is a violation of DRY. Apr 6, 2021 at 13:23
  • @BartvanIngenSchenau You clearly have a point there, but I am not concerned about duplicate code, but mostly for what is easier for other developers to understand when they read it and doesn't feel tedious to reproduce.
    – xpy
    Apr 6, 2021 at 13:42

1 Answer 1

1

Your goal

You have contradicting expectations.

On the other hand, not all microservices support all the methods and I wouldn't want the classes to have methods that are not supported.

In other words, you want to explicitly control which methods are made available, you don't want anything to be made available that shouldn't be.

[...] since I still have to write a new method for every supported method.

That is the inevitable consequence from wanting to manually define the availability of each method on each service.

Either you make the defined methods blanket available and accept that some of them might not work, or you manually make sure that only the working methods are made available. Logically, there is no other choice.

DRY

But this doesn't really solve the DRY part since I still have to write a new method for every supported method.

As was mentioned in comments by Rik D, you've slightly missed the mark on what DRY conveys. DRY doesn't mean "condense your code", it means "don't write the same thing twice".

I bolded "the same thing", because just because two things look similar does not mean they are the same thing. Your two methods are not the same thing. One (A) is the base implementation of the method, and the other (B) is making A available in B's class definition.
Those are two separate things, but they have a similar syntax since B acts as a 1-to-1 wrapper for A, which is incorrectly triggering your DRY alarm.

Your first solution

One way to solve it could be to pretend that the methods are private by adding an underscore and then just extend the ones that are supported.

"Pretending" access modifiers are different than what they are is not a good approach.

I'm no Python dev, but from what I read the "underscore prefix" approach is much more than just pretend, it actually enforces protected access to class members.

In going this route, I would suggest you first consider composition over inheritance, i.e. not deriving from BaseAPI but instead simply injecting a BaseAPI object in your MyService class and then accessing its public methods directly in the MyService class methods.
This way, you still decide which methods get expose (by deciding which MyService methods you write), without needing to rely on inheritance.

Inheritance has its purpose, but it is often overused in cases where it isn't necessary, and this case (inheritance which wants to be selective about what to inherit) seems to be such a case.

Your second solution

I'm a C# dev, not a Python dev, and I don't have experience with multiple inheritance (since C# doesn't have it). I'm no fan of multiple inheritance here (for the same reason that I'm not a fan of single inheritance in the previous paragraph), but my view on this is possibly too biased, so take it with a grain of salt.

2
  • Your source is not describing the access modifiers properly, it is actually a convention, python doesn't care about private methods docs.python.org/3/tutorial/classes.html#private-variables
    – xpy
    Apr 6, 2021 at 13:32
  • @xpy: Fair enough, my source was only a quick google, but this brings it back to my earlier "pretending isn't a great approach' point. That being said, since I'm no Python dev, I am not sure if it is conventionally accepted among Python devs that this "protected convention" is considered good practice or not.
    – Flater
    Apr 6, 2021 at 14:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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