Jump to content
  • Advertisement
Sign in to follow this  
mikeman

YAGNI-Where does it end?

This topic is 3816 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

This question popped into my head today at work, as I was getting ready to rafactor some duplicated code into a function. I'll give an example to make my point clearer. Let's imagine the dialog between 2 partner programmers, 1 producing some 'questionable' code, and the other critisizing this code:
void hit_player(int hit)
{
  Player* player=Player::GetInstance();
  switch (player->type)
  {
     case PLAYER_MORTAL:
       player->DecreaseHealth(hit);
       break;
     case PLAYER_DEMIGOD:
       player->DecreaseHealth(hit*0.2);
       break;
     case PLAYER_GOD:
       player->DecreaseHealth(hit*0.05);
  }
}


-Ehm, shouldn't we try to avoid singletons or globals? It's bad practice... -Why? We only have 1 player in the game -Yeah, but what if in the future we want to add additional players? -You Aint Gonna Need It -Okay, but Player::GetInstance() is accessible from everywhere, even unrelated modules. This is going to be bad for bug hunting. -I've checked the code I've written. Right now, it's accessed only in related modules. No worries. -Yeah, but what if in the future... -You Aint Gonna Need It -Okay. So how about this "switch" thing? -What about it? -It's brittle and error-prone. Shouldn't we use virtual functions instead? -Error-prone my arse. I checked the code. There are no errors. All possible types are handled right now. -Yeah, right now. But modules should be open for extension, closed for modification. What if in the future we need to add another type? Then we would... -You Ain't Gonna Need IT -But what if... -YOU AINT GONNA NEED IT! See, I'm having a bit of a difficult time seeing where this attitude stops. YAGNI, as defined in this page, means that "Even if you're totally, totally, totally sure that you'll need a feature later on, don't implement it now.". That means: code according to your current needs, not according to what needs you think you'll have in the future. But doesn't that mean, that whatever code compiles, run, and does what is expected to do *right now*, is good enough? Is Yagni univeral, or is there an area where it doesn't apply? Does it include overall design choises, or primary specific functionality features? Are there proven design principles(such as OCP), that should in fact "override" YAGNI and be folllowed from early on, even when the benefits are not apparent, precisely because you'll benefit from them later on? What is your opinion?

Share this post


Link to post
Share on other sites
Advertisement
Quote:
-Ehm, shouldn't we try to avoid singletons or globals? It's bad practice...
-Why? We only have 1 player in the game


False premise.

This is *not* the reason singletons are bad. It's bad due to coupling.

The code is also poorly designed:


void Player::takeDamage( int hit )
{
decreaseHealth( hit * damage_reduction );
};

Player::Player( ..., PlayerType pt )
: ...
, damageReduction( DamageTable[pt] )
{}

// Damage table is provided from somewhere, might be passed as part of parameters to constructor





Using proper OO makes the rest of discussion moot.

Quote:
See, I'm having a bit of a difficult time seeing where this attitude stops. YAGNI, as defined in this page, means that "Even if you're totally, totally, totally sure that you'll need a feature later on, don't implement it now.". That means: code according to your current needs, not according to what needs you think you'll have in the future.


There's a good and bad way to assume YAGNI.

Don't implement now is not the same as "build everything as hostile to future extensions as possible". This is what singletons and switch statements do - and they are really easy to work around.

It's the same as optimize last. It does not mean: "write the crappiest slowest poorest buggiest algorithms there are and then fix bugs as customers discover them".

This is why it's pointless to discuss or even mention things like this to management, since they'll make incredibly uninformed decisions which will devastate the project on false premises.

YAGNI does not mean abandon proper design.

Share this post


Link to post
Share on other sites
If it works, leave it for now.

That said, the conversation is a little silly.

Point 1: avoiding Player::GetInstance.

Sure, having more than one player is YAGNI, but you do need 1 player. Using a singleton and passing it in as a parameter are equally difficult/complex solutions to a problem you already need (and implement). Switching to the other solution for something you clearly need is not YAGNI; it's maybe silly since it's already done and tested, but that is rather shallow in the face of the known problems that will crop up.

Point 2: the Switch

Again, you already need something to change the effect based on type. The switch is just as difficult/complex as a virtual function or a strategy, or best yet a nice table lookup. Again, it's done and tested so leave it be, but again a shallow argument for a clearly inferior solution.


In the end, the argument here shouldn't be YAGNI, but 'work on stuff that doesn't work yet'.

Share this post


Link to post
Share on other sites
Quote:

'work on stuff that doesn't work yet'


What do you mean? Both the singleton and the switch work right now *exactly* as expected. Why change them? What proper OO techiniques offer *right now* that the existing solutions don't?

Share this post


Link to post
Share on other sites
Quote:
Original post by mikeman
Quote:

'work on stuff that doesn't work yet'


What do you mean? Both the singleton and the switch work right now *exactly* as expected. Why change them? What proper OO techiniques offer *right now* that the existing solutions don't?


You are right, perhaps I was not clear. I meant that the person arguing for YAGNI in the example, should have argued for prioritization instead.

Share this post


Link to post
Share on other sites
Quote:
Original post by mikemanWhat do you mean? Both the singleton and the switch work right now *exactly* as expected. Why change them? What proper OO techiniques offer *right now* that the existing solutions don't?


YAGNI is design.

If design document said:
- There will only ever be one player
- There will only ever be 3 difficulty settings
- There will only ever be 1 damage type
then this code is perfect.

If design document didn't say that, then it's time to have a serious talk with the coder.

Also - good coders will never code themselves into corner like this, since experience has taught them that requirements change. So they'd never use singletons and hard-coded values like this.

Share this post


Link to post
Share on other sites
You May Not Need It, But If You End Up Adding It Later It Would Be Much More Painful Than Doing It Now is probably too long to be a good acronym, but is nevertheless a good thing to keep in mind. YAGNI should never be used to excuse poor design, only to delay time-consuming design; and relative costs of adding things now versus later should always be kept in mind.

Share this post


Link to post
Share on other sites
Quote:
Original post by rip-off
Applying YAGNI from the start, player will never start as a Singleton.

QFT.

A singleton itself often violates YAGNI. Writing all the singleton boilerplate (even if that just involves grabbing some prewritten templated singleton class and using it) is already over engineering a simple task. Simply create and instance with suitable scope and pass it in as a parameter. Doing anything more complicated (ie. a singleton) is doing wasted work.

For switch statements, I almost always start with a switch and only refactor to polymorphism when it gets unweidly. A small switch like that I'd probably leave alone. You only really see the benifit if you've either 1. already got a type hirarcy in place. 2. Have more than a screen full of states and/or need to add states without touching the original code or 3. have the same switch (with different effects) in multiple methods (eg. maybe you have one in your render() and one in your update()).

Also bear in mind that YAGNI assumes that you'll be able to refactor and change things around whenever you like. If this doesn't apply (like, maybe you're designing a DB table structure and your DBA won't change it without forms written in triplicate, or maybe when you're creating a file format which will be released into the wild and you'll have to maintain support for) then planning ahead is going to be more important.

Like pretty much all programming, there are no hard and fast rules which apply all the time. The core concept of YAGNI is (IMHO) that you should design for requirements you need *right now* and avoid overcomplicating things by guessing what you'll need in the future. If you constantly question whether you really need the functionality vs. whether it just seems like a good idea then you're well on the right track.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!