9 'rules' for cleaner code | Object Calisthenics

preview_player
Показать описание

Hello everybody I'm Nick and in this video I am going to cover a ruleset called Object Calisthenics. Object Calisthenics was defined by Jeff Bay in his The Thoughtworks Anthology book and it defines 9 different rules that you can use to write and keep your code clean.

Timestamps
Intro - 0:00
Only One Level Of Indentation Per Method - 1:34
Don’t Use The ELSE Keyword - 5:14
Wrap All Primitives And Strings - 5:53
First Class Collections - 6:39
One Dot Per Line - 9:32
Don’t Abbreviate - 13:40
Keep All Entities Small - 15:56
No Classes With More Than Two Instance Variables - 18:12
No Getters/Setters/Properties - 20:36

Don't forget to comment, like and subscribe :)

Social Media:

#cleancode #cleancoder #objectcalisthenics
Рекомендации по теме
Комментарии
Автор

the most important thing about rules is that you know you're breaking them and why you're breaking them. The rules exist for people who can't yet recognize the problems they're inviting when they do.

seanon
Автор

These 'rules' and 'guidelines' are getting more and more scholastic through the years. One dot per line, one indentation per method, 5 methods per class. I was really interested what was going to be next: one IDE per computer or maybe one rubber duck per debug session? Really interesting.

Jokes aside, I watched few of your videos and you have good content dude. The level of knowledge and skill is really impressive. Thank you!

henrykkaufman
Автор

I love how the example of "Don't abbreviate" contains "Id" (abbreviation of identification)

TheKataan
Автор

Really good video - I appreciated that you didn't just regurgitate a list of rules, but give your opinion on how they face up to reality. Because seriously, how the hell can you fit a useful class into 50 lines of code?

jackkendall
Автор

Been waiting for this the moment I've watched your "not using else" video. Keep it up.

ycra
Автор

1. I follow this with a caveat - it's only a problem if nested indentation is more than a few lines long.
2. I don't do this, it often makes code less clear.
3. I do this sometimes, really depends on what kind of code I'm writing and how I use primitives
4. Heck no. If the collection doesn't have its own invariants, it shouldn't be newtyped. Most of the time the invariants are the owning class's, not the collection's.
5. Most of the time I do this, not always though.
6. This is good. If you can't find a reasonably short name w/o abbreviation, you're probably doing something wrong.
7. This is a pure antipattern, also known as "how to produce an unreadable codebase". Even simple behaviors must span multiple files with this approach, and that's terrible for readability.
8. This is pretty silly. Doesn't do anything good, just bloats the codebase. Keep classes small, but split them at meaningful points.
9. If you have both a getter and a setter that do nothing, it should probably be public. If it shouldn't be public, it probably should just have a setter.
In general: clean code comes from care and attention, deliberate thought. Guidelines like these can help, but they won't give you clean code automatically.

berylliosis
Автор

One level of indentation sounds like it was brought to you by the same people who want to limit code to 80 characters per line. I can see where it would be useful in an extremely restricted environment (VIM terminal), but it just feels stupid when you're working in a real IDE. Mostly I find that those hyper-compressed format rules just makes code extremely long vertically, making it harder to read and understand.

First class collections is a thing I've tried on occasion. Sometimes it works, and sometimes it just creates more of a headache (particularly if you end up with 'using' imports).

One dot per line starts failing when you're trying to do complicated linq queries on nested dictionaries or the domain objects you wrap primitives in, such that the objects you're working with aren't directly connected to each other. It's an ideal that doesn't match the reality of difficult algorithms.

Don't abbreviate, but be careful about dumbing things down _too_ much. AddUser() is a lot more informative than just Add() (depending on how the class is used). Basically, name things so that they make sense no matter the context, both inside and outside the class.

Small entities, against a rule made by people who never had to deal with a switch statement, or a state machine, or anything where the complexity is inherent to the algorithm. Extracting methods is often a good rule of thumb, but the risk is losing context in the extracted method vs keeping it with the rest of the code.

"No class with more than two instance variables" — This makes me laugh a bit. Even with your example, what happens when you have to keep track of the game character's stats? Standard D&D has 6 attributes, and tons of derived stats. Maybe you encapsulate that in a 'Stats' class, but now the stats class itself has multiple instance variables. Even if I'm just writing a simple wrapper class or record, I almost always need more than two properties. It doesn't seem like it would ever be workable.

On the last item, I can definitely see not allowing setters, but removing getters/properties is just silly. You put in an API that controls how the fields can be manipulated, but you shouldn't add obstacles to how the properties can be _used_ .

David-idjw
Автор

I tell this always to my members and students that clean codes makes you like doing storytelling rather than following complex instructions.

SetupGame ()
ScoreTo (team)
FinishGame ()
GetMvp ()
CloseGame ()
End ()

Oh yeah, your explanations is really great. Some of them are still violating because i dont have choice but to make it work and readable.

codingwithgyver
Автор

A nice overview. Nick clearly shows that things in practice often are not as clear-cut as theory would like to have it. When it comes to method count and SRP, I would recommend only counting the *public* methods. If the public API of a class does too much, it probably violates SRP. How many methods are used internally to achieve this goal - I don't care. This allows free application of the extract method refactoring, provided that your extracted methods are private (and in 99% of all cases, extracted methods should be private).

AlanDarkworld
Автор

Nick seems to get the bigger picture. Respect! The dogma / rules all boil down to DEP / DIP (dependencies are dangerous; limit and protect your code accordingly). All the rules are approaches to dependency management.

skewty
Автор

Number 9 is a big one people don't talk about enough. In OOP you not only want objects to have separation of responsibility but also *self reliance*. You generally shouldnt ask for data to do a job, you should tell the object with that data to do the job for you.

Sometimes a job requires some data to do, when possible it should be through a function parameter, not setting public variables. Sometimes you have to get some data, ideally it should be through a function that returns an interface with the correct "do your job" methods inside it.

The ultimate goal for maintainable OOP code is to pass around explicit data (especially mutatable state data) as little as possible.

PoorlyMadeSweater
Автор

I'm so happy that I found your channel! Amazing video as always!

_Bence
Автор

"What you think I'm editing? A crocodile? No." Lol nice one. Great video on some good disciplines.

jannickbreunis
Автор

_"A CROCODILE?"_ - Nick Chapsas 2021

DanteDeRuwe
Автор

Nick your video is totally killing! Thanks for sharing it! Personally I like all this practices (including even 7 and 8) but with numbers adjusted to the team needs (maybe even several tresholds / levels) would nice to have configurable local linter for all the rules (integrated with IDE)

bodek
Автор

15:23 I liked the way how you laid out the crocodile here :P

marcelius
Автор

Hi Nick, nice tips i will try to keep them in mind, thank you from Portugal

Ruisrd
Автор

The absolute best part of this video. "... what do you think I am adding here? A crocodile?" rofl

sasilverain
Автор

For any instance of a class where the class is a "leaf" class. (No dependencies to injected behaviour in the constructor)
If your method has a return type: It can only have the purpose of returning said type. (No (side) effects)
If your method is a "void" return type, it can only have 1 (side) effect.

This forces you to divide your class methods into easily testable "chunks", and ensures your methods stay small.
Now for anything that isn't a leaf, try to adhere to the same principles, but of course, when you need to do multiple actions, like, CalculateScore, CheckWinCondition, CheckLossCondition, etc.
But each of these examples will have adhere to the rule, so there will only ever be 1 action associated with a method call. This makes the code readable, because you can easily anticipate what a method does, and method complexity and size, will always remain low.

It makes your "abstraction layers" or "paragraph levels" easier to read through.
Following this rule of thumb, makes your code read like words on a page, rather than "code".

And, it doesn't specify an arbitrary "line count" or min/max setting, merely, that the code has to be cohesive, as a "single effect" is the purpose. (Single responsibility pattern)

Forgive the pseudo code here!

Say:
public class Player{
private int _score;

public void method ScoreIncrease()
{
_score++:
CheckLoss();
CheckWin();
}

private void CheckLoss(){
//whatever
}

private void CheckWin(){
Whatever;
}
}

Violates this pattern, in the public method, it has 3 actions-> increase score, check for a win, check for a loss.
Now you must do all three.

So you have to extract each private method into a separate leaf class, That controls how to do whatever you private methods are doing.

So it becomes:

public class Player{

private int _score;
private readonly IWinChecker _winChecker;
private readonly ILossChecker _lossChecker;

public Player(IWinChecker winChecker, ILossChecker lossChecker)
{
_winChecker = winChecker;
_lossChecker = _lossChecker;
}

public void method ScoreIncrease()
{
_score++:


}

Now Player is NOT a leaf class, and can have multiple actions, but the effects, are split out into separate classes, and the behaviour is maintained separate from the Player class.
Checking for a win or a loss, may also well use variables, that do not belong in the Player class, like a limit, that may be map specific? or difficulty specific, etc.
now these values can be DI'ed in where they belong. It forces you to separate into Single responsibility, without actually thinking about code design.

Also the naming convention:
All interfaces, abstracts, classes, etc. MUST BE NOUNS.
All method names MUST BE VERBS.

This makes everything make way more sense, when reading.

mortenbork
Автор

Crocodile joke got my like 😅
Thanks for sharing!

ericserafim
join shbcf.ru