5
\$\begingroup\$

I've been told that using God objects at all is a Bad Thing™

In object oriented languages, God objects know all, they control too much. I'm trying to build a game (or for the scope of this question a generic app with a GUI) and I'm using a Main object that holds all the other objects needed to make it all work.

At the moment (and probably forevermore), my main.py module contains only functions that initialise the other modules in order to setup my app.

The other modules setup are managers of different things and most inherit from a class ManagerBase which among other things can retrieve other managers and from that, the contents of said managers.

#Import modules
import pygame
import sys, os

import assets.config.config_manager
import assets.events.event_manager
import assets.events.ai_event_manager
import assets.font.font_manager
import assets.ui.subscription_manager
import assets.ui.screen
import assets.ui.keyboard_injector
import assets.entity.entity_manager
import assets.databin

CAPTION = "Generic Application"

class Main(object):
  def __init__(self):
    self.args = sys.argv
    self.debug = "debug" in self.args

  def init_databin(self):
    self.databin = assets.databin.Databin()

  def init_config_manager(self):
    self.config_manager = assets.config.config_manager.ConfigManager()
    self.fps_limit = self.config_manager["video_config","fps_limit"]
    self.show_fps = self.config_manager["video_config", "show_fps"]
    if not self.show_fps:
      self.blit_fps = lambda: None

  def init_screen(self):
    pygame.init()
    self.screen = assets.ui.screen.Screen(pygame.display.set_mode(*self.config_manager.get_screen_properties()), pygame)
    pygame.display.set_caption(CAPTION)
    if self.config_manager["video_config"]["screen_properties"]["fullscreen"]:
      pygame.mouse.set_visible(False)
    self.screen.blit(self.screen.old_im_load(os.path.join("assets", "loading.png")), (0,0))
    self.update_screen()
    self.clock = pygame.time.Clock()

  def init_event_manager(self):
    self.event_manager = assets.events.event_manager.EventManager()
    self.event_manager.add_events()

  def init_keyboard_injector(self):
    self.keyboard_injector = assets.ui.keyboard_injector.KeyboardInjector()

  def init_ai_event_manager(self):
    self.ai_event_manager = assets.events.ai_event_manager.AiEventManager()
    self.ai_event_manager.add_events()

  def init_entity_manager(self):
    self.entity_manager = assets.entity.entity_manager.EntityManager()

  def init_font_manager(self):
    self.fonts = assets.font.font_manager.FontManager()
    self.fonts.register_font("fps", "verdana", 12)

  def init_subscription_manager(self):
    self.subscription_manager = assets.ui.subscription_manager.SubscriptionManager()
    self.subscription_manager.load_subscription()

  def run(self):
    while 1:
      self.keyboard_injector.run()
      self.event_manager.parse_events(pygame.event.get())
      self.subscription_manager.run_subscription()
      self.clock.tick(self.fps_limit)
      self.blit_fps()
      self.update_screen()

  def blit_fps(self):
    try:
      count = int(self.clock.get_fps())
    except OverflowError:
      count = "Infinate?"
    fps = self.fonts["fps"].render("FPS: %s" %(count), True, (255,255,255))
    self.screen.blit(fps, (10, 30))#, no_scale = True)

  def update_screen(self):
    pygame.display.update(self.screen.blit_rects_old)
    pygame.display.update(self.screen.blit_rects)
    self.screen.blit_rects_old = self.screen.blit_rects
    self.screen.blit_rects = []

def main():
  global main_class
  main_class = Main()
  main_class.init_databin()
  main_class.init_config_manager()
  main_class.init_screen()
  main_class.init_event_manager()
  main_class.init_keyboard_injector()
  main_class.init_ai_event_manager()
  main_class.init_entity_manager()
  main_class.init_font_manager()
  main_class.init_subscription_manager()
  main_class.run()


if __name__ == "__main__":
  os.environ['SDL_VIDEO_CENTERED'] = '1'
  if "debug" in sys.argv:
    try: import cProfile as profile
    except ImportError: import profile
    profile.run('main()')
  else:
    main()

ManagerBase uses something dreaded... import __main__ in order to get access to the main class.

import __main__

class ManagerBase(object):
    def get_main_class(self):
        return __main__.main_class

    def get_databin(self):
        return self.get_main_class().databin

    def get_pygame(self):
        return __main__.pygame

    def get_main_dict(self):
        return __main__.__dict__

    def get_config_manager(self):
        return self.get_main_class().config_manager
\$\endgroup\$
  • 2
    \$\begingroup\$ As we all want to make our code more efficient or improve it in one way or another, try to write a title that summarizes what your code does, not what you want to get out of a review. \$\endgroup\$ – Jamal Jan 1 '16 at 20:02
  • \$\begingroup\$ I don't know the intricacies of pygame, but python has facilities for exporting package level variables in your __init__.py file. Check out this comment for more. Rather than import __main__, you could setup your main_class in __init__.py and then use from <game> import main. Check out flask's __init__.py and __main__.py for inspiration. \$\endgroup\$ – svass Jan 3 '16 at 3:15
5
\$\begingroup\$

According to Wiki "a god object is an object that knows too much or does too much". The general problem I have with this is: What means "too much"? Your question is about searching an absolute statement as we already know we can only find absolute statements within very restricted areas (and that's what they all have in common) that are not related to reality. As soon as we deal with real world applications we have to deal with uncertainty. That is because we derive OO models from reality as we perceive it. And this can be error prone.

To escape this dilemma in computer science some principles are discovered that lead to a step by step improvement of source code. They are called S.O.L.I.D. principles. If you violate a principle your source code becomes worse. So the target is to violate the principles as less as possible. So easy as I say it: The violation of these principles is an identification problem that sometimes becomes very very difficult.

In the case of the so called "God class" the "S" of these principles is addressed: The single responsibility principle (SRP). It says that one code fragment (module, class, method) should only have one responsibility. BTW this is applicable to other programming paradigms as well. A "God class" seems to have at least more than one responsibility. That can be said for sure. Anything else is popular speech if someone says "God class".

So working with SRP your code will improved step by step by identifying violations of this principle and eliminate them. That is by consolidating redundant responsibility and vice versa by separating different responsibilities.

But the whole thing only works if you identify the violation. And that's the core. To identify a violation you look for indicators:

  1. real redundant code fragments
  2. a bug that was not fixed everywhere because of code redundancy
  3. a bug that was fixed but broke the application at another place
  4. long classes, long methods
  5. a lot of object local variables
  6. deep nesting
  7. objects that do a lot of different things
  8. ...

I want to underline that these are only indicators. A long method method for example can do only one thing: initialize a hashmap with key value pairs. Although you would have thought about solving this another way it's not violoating SRP.

So if you have an indicator you can make a thought experiment if there is a violation. If you think you have redundant responsibilities then you should think about a new business requirement that changes one code fragment and ask yourself should the other change as well. This you should discuss with the business people. BTW consolidating responsibilities is much harder that separating them because this may break the application as one redundant code fragment will be omitted.

So back to your question: A class does not become holy or a god. It will become less godly by eliminating violations of SRP. Theoretically the class becomes godless when an 1:1-relationship of responsibility and code fragment is reached. But to ask when there are too much responsibilities does not make sense in the context of code quality. This will only be a matter of costs to maintain the code. If it costs too much (for the business men) and the costs can be assigned to the SRP violation then certainly there are too much responsibilities.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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