I am working on a multifunctional quite old game bot with the followng implementation:

There are tons of mixins in different files/folders (each inherits GameBase). Each mixin:
- uses attributes + HTTP base for sending personal api requests, standartizing and processing the responses
- implements some specific activity for a sublocation,
- has a clear single responsibility: handling a specific action or group of actions related to it's sublocation.
- for example really, StreetLocation has more than 20 subhandlers (handler names in examples below are changed, to hide the originals)
- the self.eat() here is preferable that self.stomach.eat() - it should be delegated internally.
- name of functions are unique and do not cause MRO issues and do not rely on inheritance order right now.

class GameBase:  # contains all available bot attributes + HTTP communication base methods

class *Handler(GameBase)  # ~50 different hanldlers, ~20 for StreetLocation

MixinGroups:
    class StreetLocation(ArenaHandler, FightHandler, CrystalHandler, TalkHandler, ........)
# inherits every sublocation handler + organizes/controls them. 
# Provides high-level API methods to the bot.

    class HouseLocation(SomeHouseHandler1, SomeHouseHandler2)
# inherits sublocation handlers + organizes/controls them.
# Provides high-level API methods to the bot.

    class ShopLocation(SomeShopHandler1, SomeShopHandler2)
# inherits sublocation handlers + organizes/controls them. 
# Provides high-level API methods to the bot.

class GameBot(StreetLocation, HouseLocation, ShopLocation)
    # Final bot interface. Orchestrates game actions over timers and high-level apis provided by MixinGroups (big-location mixins), decides what to call and how to behave in all.

The problem is about code organization. In this case, I need to use a call from a parallel mixin. I need to add a method to StreetLocation.TalkHandler (TalkHandler is inherited by the mixin group StreetLocation), e.g. TalkHandler.get_angry_in_talk(). Under certain conditions, it must call StreetLocation.FightHandler.headshot().

Importantly:
- the call should use self.headshot(),
- I cannot override headshot() in TalkHandler and, I should not care about headshot implementation being in TalkHandler.
- I cannot break the existing MRO with adding or dropping layers and should not rely on the order of inheritance,
- TalkHandler should remain focused on talking-related actions, but be able to use parallel-mixin method call.
- IDEs and linters should recognize the method calls - if possible,
- I am aware of potential large object issues, but this only matters for considering better design. Mixins are always composed into location objects and mixin class itself should not be used directly outside of final class - only final class is used. And this call should not change the inheritance tree.

Currently, there are no MRO issues, because method names do not conflict. Each file handles a specific action, with clear responsibilities, and unclassified helper functions are not desired.

Constraints & goals:
1. get_angry_in_talk() must call self.headshot() correctly and under self..
2. TalkHandler should not acquire/implement unrelated responsibilities.
3. Existing MRO must remain valid; no reliance on inheritance order.
4. Avoid introducing placeholder/notimplemented methods in Base class - Base class should not have notimplemented methods just specificly to this case (or should?).
5. IDE autocompletion and method recognition should remain working.

Questions:
1. Is it reasonable to implement this method (get_angry_in_talk) inside TalkHandler without causing side effects in other mixins or MRO issues? (as the pylance could not find whence self.headshot comes from, but code will work fine)
2. What are best practices for calling methods from parallel mixins in Python while keeping responsibilities separated?
3. Would it be better to place the method at the “location” level (StreetLocation) instead of in the sublocation mixin? - But I afraid this will break responsibility division, as it still belongs to the TalkHandler.

(like I have conflict between Q1 and Q3 and need recommendation about Q2)

I find this location/sublocation separation with orchestration quite clean in a matter that I can easily find what code isrelated to some specific location/sublocation and easily modify it (at least, if it does not require parallel-mixin call - it's pretty easy to find/modify something).

If this design is considered poor, what alternatives exist that do not rely on tons of unrelated helper functions, with strict division on responsibilities, having totally near 50 sublocations, where ~20 is only a StreetLocation handler? (assuming that I do not have any wish to overwrite so many someone's code, which is finely separated by responsibilities in my opinion)

0

Your Reply

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.