Refactoring
Refactor! Refactor! Refactor!
How often do you refactor your code? What's your process? I don't refactor enough. Sometimes I spend too long refactoring something that doesn't need it.
#1) Identify small problems - refactoring repeated code into functions, reorganizing variables. I find that starting with this leads naturally into
#2) Microscopic re-engineering - Without making any major engineering changes, reorganize within a class. Take advantage of nested classes, isolate code to respective classes. Maybe a method would belong better in a different class, to make a relationship clearer.
#3) Massive re-engineering - so you have a functional prototype, and now you need to make it more extensible, flexible, or freeze its implementation for a while. Break classes up. Look to design patterns to simplify relationships, but don't re-engineer without clearly stated reasons.
I like to start with small nagging issues, and I find that by clearing up areas of code that have been bugging me for a while, there is a natural outward growth into more major revisions; the necessary intensive revisions tend to make themselves apparent by focusing on small issues first.
Anything I'm missing? Any tips? Often I delay refactoring for too long because it feels like housekeeping, but I try and stay in a cycle of implementing functionality, and once a new (major) feature has been added, either rewrite that portion of code from scratch with a new design (you learn a lot of lessons writing the code the first time around, and don't want to locked into the 'hacky' implementation you used to just get it working) or if the code is clear enough, simply refactor. How do you find the balance between rewriting a component and simply refactoring what you already have? What do you consider?
You need to define your terms.
"Refactoring" means different things to different people. It has also become a buzzword.
There are several definitive books on the subject that you should read if you have not.
Check out both "Refactoring: Improving the Design of Existing Code" and "Refactoring to patterns", which really sparked a much of the current excitement over it.
I like TDD which promotes continuous refactoring* for personal projects and for long-term code, but it is a bad fit for game code that is single-use code.
I dislike "This component is bad so I need to refactor* it" and similar expressions. Generally they mean "rewrite", not "refactor". Of course, your definition may vary.
(* Depending on how you define "refactor". )
"Refactoring" means different things to different people. It has also become a buzzword.
There are several definitive books on the subject that you should read if you have not.
Check out both "Refactoring: Improving the Design of Existing Code" and "Refactoring to patterns", which really sparked a much of the current excitement over it.
I like TDD which promotes continuous refactoring* for personal projects and for long-term code, but it is a bad fit for game code that is single-use code.
I dislike "This component is bad so I need to refactor* it" and similar expressions. Generally they mean "rewrite", not "refactor". Of course, your definition may vary.
(* Depending on how you define "refactor". )
I tend to develop rules of thumbs to indicate possible problems:
1) Large source file size, or large number of imports/includes: possible failure to separate concerns, violation of single purpose principle: analyze and split into multiple objects if necessary
2) Repeated code: move to function or method
3) Deep inheritance heirarchy: possible violation of single purpose principle, or overuse of inheritence: analyze and re-design to use composition if necessary.
4) Vague class names: possible violation of single purpose principle: check for re-design
5) Not unit testable: bad dependency injection or tight coupling: extract interfaces and add in appropriate patterns
6) Deeply nested code blocks: doing too much work in a single function, leading to large/unreadable functions: separate logic between methods or functions.
7) Large comment blocks: code is not self documenting: analyze and re-design
Basically, my approach is to look for signs of bad design, analyze whether it violates some coding principles, and then either re-factor or re-design, depending on the situation.
1) Large source file size, or large number of imports/includes: possible failure to separate concerns, violation of single purpose principle: analyze and split into multiple objects if necessary
2) Repeated code: move to function or method
3) Deep inheritance heirarchy: possible violation of single purpose principle, or overuse of inheritence: analyze and re-design to use composition if necessary.
4) Vague class names: possible violation of single purpose principle: check for re-design
5) Not unit testable: bad dependency injection or tight coupling: extract interfaces and add in appropriate patterns
6) Deeply nested code blocks: doing too much work in a single function, leading to large/unreadable functions: separate logic between methods or functions.
7) Large comment blocks: code is not self documenting: analyze and re-design
Basically, my approach is to look for signs of bad design, analyze whether it violates some coding principles, and then either re-factor or re-design, depending on the situation.
I want to second the suggestion of Refactoring: Improving the Design of Existing Code, It's a classic, and it really helped me hone my style quite a bit. It'll change the way you code, I can virtually guarantee it. I highly recommend it.
IMO, the most important question regarding refactoring is "why am I doing this?".
Refactoring is not inherently bad, but there's no point refactoring unless you have a compelling reason to do so.
Let's say I have a library. It's reasonably well encapsulated, it performs ok and it's been used in a few products, so I'm pretty sure it does what it's supposed to. But I wrote it ages ago, and when I look at the code now, I cringe. The internal architecture is hideous, it uses it's own collection classes, there's no consistent coding convention and when there is, (cue dramatic music) it's using hungarian notation! (and not even the less bad kind) Yes, truly this code is an abomination unto the flying spaghetti monster. It kills kittens, makes hitler happy and so on. A prime candidate for a refactor, yes?
Well, maybe. But what's the benefit to the project? Granted, if I find a bug in it, I'd probably clean up the area I'm fixing. But even if there's a fundamental extra requirement placed on the code, I'd start by seeing if I could wrap the existing functionality.
The flip side to that argument, is that if you're doing a major refactor, breaking changes should really break the code (i.e. it shouldn't compile anymore). This forces you to examine how you're using the code and make sure your old use will be compatible with the new lib.
Refactoring is not inherently bad, but there's no point refactoring unless you have a compelling reason to do so.
Let's say I have a library. It's reasonably well encapsulated, it performs ok and it's been used in a few products, so I'm pretty sure it does what it's supposed to. But I wrote it ages ago, and when I look at the code now, I cringe. The internal architecture is hideous, it uses it's own collection classes, there's no consistent coding convention and when there is, (cue dramatic music) it's using hungarian notation! (and not even the less bad kind) Yes, truly this code is an abomination unto the flying spaghetti monster. It kills kittens, makes hitler happy and so on. A prime candidate for a refactor, yes?
Well, maybe. But what's the benefit to the project? Granted, if I find a bug in it, I'd probably clean up the area I'm fixing. But even if there's a fundamental extra requirement placed on the code, I'd start by seeing if I could wrap the existing functionality.
The flip side to that argument, is that if you're doing a major refactor, breaking changes should really break the code (i.e. it shouldn't compile anymore). This forces you to examine how you're using the code and make sure your old use will be compatible with the new lib.
if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
Something I picked up (don't know where) was that when you have a change/feature you need to add, refactor the code so that making that change is easy, then add it.
This tends to mean that:
1. You only refactor code when needed, so don't get bogged down chaning the whole system when you don't need to.
2. Refactoring gets you more familiar with the existing code so your actual change will be made more in line with it.
3. Your refactorings end up being targeted, rather than vauge hand-wavy "it's better now" refactoring that can often make things worse.
4. Since you'll be testing the new stuff, you're more likely to spot any mistakes and bugs you've introduced accidentally.
ymmv.
This tends to mean that:
1. You only refactor code when needed, so don't get bogged down chaning the whole system when you don't need to.
2. Refactoring gets you more familiar with the existing code so your actual change will be made more in line with it.
3. Your refactorings end up being targeted, rather than vauge hand-wavy "it's better now" refactoring that can often make things worse.
4. Since you'll be testing the new stuff, you're more likely to spot any mistakes and bugs you've introduced accidentally.
ymmv.
[size="1"][[size="1"]TriangularPixels.com[size="1"]] [[size="1"]Rescue Squad[size="1"]] [[size="1"]Snowman Village[size="1"]] [[size="1"]Growth Spurt[size="1"]]
Quote: Original post by frobI like TDD which promotes continuous refactoring* for personal projects and for long-term code, but it is a bad fit for game code that is single-use code.
I dislike "This component is bad so I need to refactor* it" and similar expressions. Generally they mean "rewrite", not "refactor". Of course, your definition may vary.
What would you say the time-frame is to be considered "long-term code"? 1-2 years minimum? Isn't that within the scope of a game-development time-frame? Forgive me if this is naive, I'm just curious - why the distinction?
As far as rewriting vs. refactoring, refactoring to my mind connotates that you are reworking an existing collection of code, vs. rewriting which sounds to me more like starting from scratch, within the borders of the component being revised.
I'll have to check out those books. Thanks for the links.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement