C
C#6mo ago
Loup&Snoop

✅ Refactoring A Class With Too Many Responsibilities

Right now, I am trying to refactor a level editor class, which currently has way too many responsibilities, and I want to fix that. My core issue comes from trying to restrict access to certain methods between my level editor and other scripts. For example, here are methods for drawing a tile in a level (in my LevelEditor class): -private DrawCommand (what to do when user issues a command to draw at the target position, including validation logic. Calls DrawWithLog) -private DrawWithLog (try to draw a tile while logging changes to undo history, calls DrawAtRegion) -public SmartDrawIngame (force draw, but also funnels logic through some singletons that may need to update internals, Calls ForceDrawIngame) -private ForceDrawIngame (outside level edit UI, other scripts can request drawing at a specific spot using other private methods. Calls DrawAtRegion) -private DrawAtRegion (actually erases anything that conflicts in different tilemaps, and draws. Calls SetTile) -private SetTile (draw the tile and associated metadata) There are several other features like this, and I want to split up responsibilities across a couple of classes (so I can also add other features). Any recommendation on how to split this without exposing everything publicly?
6 Replies
Loup&Snoop
Loup&Snoop6mo ago
tl;dr class with too many responsibilities. How to split without effectively making everything public (given code is too coupled for internal to work)
not guilty
not guilty6mo ago
refactoring is always an interesting problem, but i don't know what kind of advice you really can get on this; usually refactoring involves applying stuff like single responsibility principle, finding well known workflows, isolating components/behaviors; i would probably start at the top or at the bottom, for example if SetTile is where the rubber touches the asphalt then that method could go in a separated class with other strictly low level methods; or maybe it's the reverse, the class should be only for low level methods and it's the others that should go in separated classes; but also the fact that in the same class DrawCommand has validation logic seems out of place; or for example if DrawWithLog interacts with undo history then there's to analyze if there is enough indirection with stuff that manages undo; also what "without exposing everything publicly" means, i mean you have to have some public methods, at most you could have internal classes, but is there a real issue here? a class with a public SetTile method would be fine to me (except maybe for the name, what does 'Set' means? add? update? it's kind of a vague prefix); last but not least, are you already using some kind of overarching patterns/architecture?
Loup&Snoop
Loup&Snoop6mo ago
thanks for the input. I wanted to keep a lot of these methods private to avoid inadvertently making low level modifications without triggerent events that depend on modifying the level. But i can’t do this if I want to split these responsibilities. The method names aren’t the actual names. I’m also going to modify the function names a bit as I split them up. In terms of overarching pattern, the only major pattern here is singleton. This class is a central point of contact to block multiple classes from attempting to modify the level without proper validation. This is not to defend my choices, but to explain how I got into this situation.
not guilty
not guilty6mo ago
two of the ways to avoid calling methods you shouldn't call are using interfaces to hide the implementation and moving a class in a separate project so that it can be called by other classes in the same assembly but the main tool remains discipline, avoiding shortcuts; there is no alternative to that, probably
Loup&Snoop
Loup&Snoop6mo ago
i think breaking up my class into several classes with different responsibilities is best, so one class is just known as a general point of contact for (editor mode changes), another is a point of contact for (non-editor mode changes), and a third contains common toolbox functions for both (and is clearly not to be accessed outside of that namespace) ty for the advice
not guilty
not guilty6mo ago
(if you want to close the thread use /close)