└── README.md /README.md: -------------------------------------------------------------------------------- 1 | ## Persian presentation on youtube: 2 | [clean code - MrBug](https://youtu.be/ah3LoMeRQA4) 3 | ## Table of Contents 4 | - [Meaningful names](#meaningful-names) 5 | - [Intension-Revealing Names](#intension-revealing-names) 6 | - [avoid disinformation](#avoid-disinformation) 7 | - [Meaningful Distinction](#meaningful-distinction) 8 | - [Pronounceable Names](#pronounceable-names) 9 | - [Use Searchable Names](#use-searchable-names) 10 | - [Avoid Encoding](#avoid-encoding) 11 | - [Avoid Mental Mapping](#avoid-mental-mapping) 12 | - [Pick One Word Per One Concept](#pick-one-word-per-one-concept) 13 | - [Dont Pun](#dont-pun) 14 | - [Functions](#functions) 15 | - [Small](#small) 16 | - [Indent](#indent) 17 | - [Just one thing](#just-one-thing) 18 | - [One Level of Abstraction per Function](#one-level-of-abstraction-per-function) 19 | - [Reading Code from Top to Bottom: The Stepdown Rule](#reading-code-from-top-to-bottom--the-stepdown-rule) 20 | - [Switch Statements](#switch-statements) 21 | - [Use Descriptive Names](#use-descriptive-names) 22 | - [Function Arguments](#function-arguments) 23 | - [Command query sepration](#command-query-sepration) 24 | - [prefer exception to return error code](#prefer-exception-to-return-error-code) 25 | - [Don’t repeat your self](#don-t-repeat-your-self) 26 | - [How do you write function like this](#how-do-you-write-function-like-this) 27 | - [Comments](#comments) 28 | * [Comments Do Not Make Up for Bad Code](#comments-do-not-make-up-for-bad-code) 29 | * [Explain Yourself in Code](#explain-yourself-in-code) 30 | * [Good Comments](#good-comments) 31 | + [Legal Comments](#legal-comments) 32 | + [Informative Comments](#informative-comments) 33 | + [Explanation of Intent](#explanation-of-intent) 34 | + [Warning of concequences](#warning-of-concequences) 35 | + [TODO Comments](#todo-comments) 36 | * [Bad Comments](#bad-comments) 37 | + [Redundant Comments](#redundant-comments) 38 | + [Mandated Comments](#mandated-comments) 39 | + [Journal Comments](#journal-comments) 40 | + [Noise Comments](#noise-comments) 41 | + [Don’t Use a Comment When You Can Use a Function or a Variable](#don-t-use-a-comment-when-you-can-use-a-function-or-a-variable) 42 | + [Position Markers](#position-markers) 43 | + [Closing Brace Comments](#closing-brace-comments) 44 | + [Attributions and Bylines](#attributions-and-bylines) 45 | + [Commented-Out Code](#commented-out-code) 46 | + [Nonlocal Information](#nonlocal-information) 47 | + [Too Much Information](#too-much-information) 48 | - [Formatting](#formatting) 49 | - [Vertical Formatting](#vertical-formatting) 50 | - [The Newspaper Metaphor](#the-newspaper-metaphor) 51 | - [Vertical Openness Between Concepts](#vertical-openness-between-concepts) 52 | - [Vertical Distance](#vertical-distance) 53 | - [Dependent Functions](#dependent-functions) 54 | - [Horizontal Formatting](#horizontal-formatting) 55 | - [Indentation](#indentation) 56 | - [Team Rules](#team-rules) 57 | - [Error Handeling](#error-handeling) 58 | - [Use Exceptions Rather Than Return Codes](#use-exceptions-rather-than-return-codes) 59 | - [Write Your Try-Catch-Finally Statement First](#write-your-try-catch-finally-statement-first) 60 | - [Provide Context with Exceptions](#provide-context-with-exceptions) 61 | - [Don’t Return Null](#dont-return-null) 62 | - [Don’t Pass Null](#dont-pass-null) 63 | - [Boundaries](#boundaries) 64 | - [Using Third-Party Code](#using-third-party-code) 65 | - [Exploring and Learning Boundaries](#exploring-and-learning-boundaries) 66 | - [Using Code That Does Not Yet Exist](#using-code-that-does-not-yet-exist) 67 | - [Clean Boundaries](#clean-boundaries) 68 | - [Unit Tests](#unit-tests) 69 | - [The Three Laws of TDD](#the-three-laws-of-tdd) 70 | - [Clean Tests](#clean-tests) 71 | - [One Assert per test](#one-assert-per-test) 72 | - [F.I.R.S.T](#first) 73 | - [Classes](#classes) 74 | - [Encapsulation](#encapsulation) 75 | - [Classes Should be Small](#classes-should-be-small) 76 | - [The Single Responsibility Principle](#the-single-responsibility-principle) 77 | - [Cohesion](#cohesion) 78 | - [Maintaining Cohesion Results in Many Small Classes](#maintaining-cohesion-results-in-many-small-classes) 79 | - [Organizing for change](#organizing-for-change) 80 | 81 | 82 | 83 | 84 | ## Meaningful names 85 | ### Intension-Revealing Names 86 | 87 | Bad: 88 | ```java 89 | int a; 90 | ``` 91 | 92 | Good: 93 | ```java 94 | int downTimeCounterToLifeEnded; 95 | ``` 96 | ### avoid disinformation 97 | 98 | Bad: 99 | ```java 100 | int a = l; 101 | if ( O == l ) 102 | a=O1; 103 | else 104 | l=01; 105 | ``` 106 | 107 | Good: 108 | ```java 109 | int allMrBugIssues = loadOfWeb; 110 | 111 | int zeroNumberOfServer= 0; 112 | int oneNumberOfServer= 1; 113 | int originalNumberOfBugs= 2; 114 | int originalNumberOfHumer= 3; 115 | 116 | if ( originalNumberOfHumer == loadOfWeb ) 117 | allMrBugIssues=originalNumberOfBugs; 118 | else 119 | loadOfWeb=oneNumberOfServer; 120 | ``` 121 | ### Meaningful Distinction 122 | 123 | Bad: 124 | ```csharp 125 | string deadManProperty; 126 | string deadManInfo; 127 | string deadManData; 128 | ``` 129 | 130 | Good: 131 | ```csharp 132 | string killedManId; 133 | string nameOfKilledMan; 134 | string allInfoAboutKilledWoman; 135 | ``` 136 | 137 | 138 | ### Pronounceable Names 139 | 140 | Bad: 141 | ```csharp 142 | string ymdstr = datetime.today().strftime("%y-%m-%d"); 143 | ``` 144 | 145 | Good: 146 | ```csharp 147 | string currentTimeOfHappyMan = datetime.today().strftime("%y-%m-%d"); 148 | ``` 149 | 150 | ### Use Searchable Names 151 | 152 | Bad: 153 | ```java 154 | // What is the number 86400 for again? 155 | human.sleep(86400) ; 156 | ``` 157 | 158 | Good: 159 | ```java 160 | // Declare them in the global namespace for the module. 161 | int SECONDS_IN_A_DAY = 60 - 60 - 24 ; 162 | human.sleep(SECONDS_IN_A_DAY) ; 163 | ``` 164 | 165 | ### Avoid Encoding 166 | 167 | Bad: 168 | ```csharp 169 | int iHumanCapacity= 1; 170 | string strMyName= "MrBug"; 171 | DateTime dLifeLength; 172 | bool impCalculateLifeSuffering(int nPeopleArroundYou){ 173 | bool bSuffering=false; 174 | if(nPeopleArroundYou > iHumanCapacity) 175 | bSuffering=true; 176 | 177 | return bSuffering; 178 | 179 | } 180 | ``` 181 | 182 | Good: 183 | ```csharp 184 | int humanCapacity= 1; 185 | string myName= "MrBug"; 186 | DateTime LifeLength; 187 | 188 | bool CalculateLifeSuffering(int PeopleArroundYou){ 189 | bool Suffering=false; 190 | if(PeopleArroundYou > 0) 191 | Suffering=true; 192 | 193 | return Suffering; 194 | 195 | } 196 | ``` 197 | 198 | ### Avoid Mental Mapping 199 | 200 | Bad: 201 | ```python 202 | bestTimesOfDay = ("Morning", "Befor Morning", "After Morning") 203 | 204 | for item in bestTimeOfDay: 205 | #do_stuff() 206 | #do_some_other_stuff() 207 | 208 | # Wait, what's `item` again? 209 | print(item) 210 | ``` 211 | 212 | Good: 213 | ```python 214 | bestTimeOfDay = ("Morning", "Befor Morning", "After Morning") 215 | 216 | for timeOfDay in bestTimeOfDay: 217 | #do_stuff() 218 | #do_some_other_stuff() 219 | 220 | print(timeOfDay) 221 | ``` 222 | 223 | ### Pick One Word Per One Concept 224 | 225 | Bad: 226 | ```python 227 | FetchHumanOverthinkingData(sadness, happiness) 228 | 229 | GetHumanOverthinkingData(sadness, happiness) 230 | 231 | RetrieveHumanOverthinkingData(sadness, happiness) 232 | ``` 233 | 234 | Good: 235 | ```python 236 | GetHumanOverthinkingData(sadness, happiness) 237 | ``` 238 | 239 | ### Dont Pun 240 | 241 | Bad: 242 | ```python 243 | time = calculateMyBirthday() 244 | 245 | # ... some code are happening .... 246 | 247 | time = calculateDeathTime() 248 | 249 | ``` 250 | 251 | Good: 252 | ```python 253 | timeOfBirthday = calculateMyBirthday() 254 | 255 | # ... some code are happening .... 256 | 257 | timeOfEndingThisShit = calculateDeathTime() 258 | ``` 259 | 260 | 261 | 262 | ## Functions 263 | ### Small 264 | 265 | Bad: 266 | ```java 267 | int calcBeautyOfLife(){ 268 | // here is full of messy condistion and long over 20 lines 269 | // ... 270 | // .. 271 | // .. 272 | // .. 273 | return 0; 274 | } 275 | ``` 276 | 277 | Good: 278 | ```java 279 | int calcHappinessOfLife(){ 280 | if(isHumanAlive) 281 | return 0; 282 | // keep it small and simple 283 | } 284 | ``` 285 | 286 | ### Indent 287 | 288 | Bad: 289 | ```csharp 290 | string howCanIBeAGentleman(){ 291 | for ... 292 | if ... 293 | for ... 294 | if ... 295 | for ... 296 | if ... 297 | return "you can not !" 298 | } 299 | ``` 300 | 301 | Good: 302 | ```csharp 303 | // break into multi function and max indent of each function is 2 304 | int howCan(){} 305 | int IBe(){} 306 | string AGentleman(){ return "you can not !" } 307 | ``` 308 | 309 | ### Just one thing 310 | 311 | Bad: 312 | ```csharp 313 | string getFullPackageOfData(string name){ 314 | // some code to eval life problems 315 | for ... 316 | if ... 317 | 318 | // some code to get information about coffee 319 | for ... 320 | if ... 321 | 322 | // some another junk code to eval nothing 323 | ... 324 | ... 325 | 326 | } 327 | ``` 328 | 329 | Good: 330 | ```csharp 331 | string getCoffeeInfo() { } 332 | 333 | long evalLifeProblems() { } 334 | 335 | void evalNothing() { } 336 | 337 | ``` 338 | 339 | ### One Level of Abstraction per Function 340 | 341 | Bad: 342 | ```csharp 343 | void multiLevelOfAbstraction(){ 344 | // getNameOfUser 345 | for ... 346 | if ... 347 | 348 | // check user have a lot of money or not 349 | for ... 350 | if ... 351 | } 352 | ``` 353 | 354 | Good: 355 | ```csharp 356 | string getNameOfUser() { } 357 | 358 | bool hasAlotOfMoney() { } 359 | ``` 360 | 361 | ### Reading Code from Top to Bottom: The Stepdown Rule 362 | 363 | Bad: 364 | ```csharp 365 | getUserInfo() 366 | getName() 367 | getDataFromServer() 368 | ``` 369 | 370 | Good: 371 | ```csharp 372 | getDataFromServer() 373 | getUserInfo() 374 | getName() 375 | 376 | 377 | ``` 378 | 379 | ### Switch Statements 380 | 381 | Bad: 382 | ```java 383 | void deadManOrLiveWoman(){ 384 | switch ... 385 | case: 386 | 387 | case: 388 | } 389 | ``` 390 | 391 | Good: 392 | ```java 393 | void deadManOrLiveWoman(){ 394 | 395 | } 396 | ``` 397 | 398 | ### Use Descriptive Names 399 | 400 | Bad: 401 | ```java 402 | int handle(string life){ 403 | // some complecated code to calc value of input 404 | // ... 405 | // ... 406 | // ... 407 | 408 | return 0; 409 | } 410 | ``` 411 | 412 | Good: 413 | ```java 414 | int calculateValueOfLife(string life){ 415 | // some complecated code to calc value of input 416 | // ... 417 | // ... 418 | // ... 419 | 420 | return 0; 421 | } 422 | ``` 423 | 424 | ### Function Arguments 425 | 426 | Bad: 427 | ```java 428 | int humanDetection(int name, int job, int personality, int degree, int lastTweet, ...){ 429 | return 0; 430 | } 431 | ``` 432 | 433 | Good: 434 | ```java 435 | int humanDetection(){ 436 | return 0; 437 | } 438 | // or max args =3 439 | int humanDetection(int name, int job, int personality){ 440 | return 0; 441 | } 442 | ``` 443 | 444 | ### Side effects 445 | 446 | Bad: 447 | ```java 448 | int evalLifeProblems(){ 449 | changeLifePain(); 450 | changeHumanSadness(); 451 | changeAllOfPeople(); 452 | 453 | for ... 454 | if ... 455 | 456 | return 99999999999999999999999999999999999 457 | } 458 | ``` 459 | 460 | Good: 461 | ```java 462 | int evalLifeProblems(){ 463 | 464 | for ... 465 | if ... 466 | 467 | return 99999999999999999999999999999999999 468 | } 469 | ``` 470 | 471 | ### Command query sepration 472 | 473 | Bad: 474 | ```java 475 | string timeToDeath(){ 476 | If(Set("username","you")){ 477 | ... 478 | } 479 | return "i am cpu not god !" 480 | } 481 | ``` 482 | 483 | Good: 484 | ```java 485 | string timeToDeath(){ 486 | if(attributeExist("username")){ 487 | ... 488 | } 489 | else 490 | setAtrribute("username","you") 491 | 492 | return "i am cpu not god !" 493 | } 494 | ``` 495 | 496 | ### prefer exception to return error code 497 | 498 | Bad: 499 | ```java 500 | int findCrazyManInClass(){ 501 | if(!isMan()) 502 | return 1; 503 | 504 | if(!isInClass()) 505 | return 2; 506 | 507 | if(!isCrazy()) 508 | return 3; 509 | 510 | return 1000; 511 | } 512 | ``` 513 | 514 | Good: 515 | ```java 516 | int findCrazyManInClass(){ 517 | try{ 518 | ... 519 | 520 | return 1000; 521 | 522 | } 523 | catch(Error){ 524 | return 0; 525 | } 526 | 527 | } 528 | ``` 529 | 530 | ### Don’t repeat your self 531 | 532 | Bad: 533 | ```java 534 | string coolFunction(){ 535 | return "i am not cool"; 536 | }; 537 | 538 | copyOfCoolFunction(){ 539 | return "i am not cool"; 540 | } 541 | copyOfCoolFunction(){ 542 | return "i am not cool"; 543 | } 544 | copyOfCoolFunction(){ 545 | return "i am not cool"; 546 | } 547 | copyOfCoolFunction(){ 548 | return "i am not cool"; 549 | } 550 | copyOfCoolFunction(){ 551 | return "i am not cool"; 552 | } 553 | ``` 554 | 555 | Good: 556 | ```java 557 | string coolFunction(){ 558 | return "i am not cool"; 559 | }; 560 | 561 | // import cool function and use it 562 | coolFunction(); 563 | 564 | ``` 565 | 566 | 567 | 568 | ## Comments 569 | 570 | ### Comments Do Not Make Up for Bad Code 571 | 572 | Bad: 573 | ```python 574 | # a is sum of you and me 575 | a = b + c 576 | ``` 577 | 578 | Good: 579 | ```python 580 | we = you + me 581 | ``` 582 | 583 | 584 | ### Explain Yourself in Code 585 | 586 | Bad: 587 | ```java 588 | // here is function explaination 589 | // too boring 590 | // . . . 591 | // . . . 592 | iAmComplicatedFunction() 593 | ``` 594 | 595 | Good: 596 | ```java 597 | simple() 598 | function() 599 | toUnderstand() 600 | withoutComments() 601 | ``` 602 | 603 | 604 | ### Good Comments 605 | 606 | #### Legal Comments 607 | 608 | Good: 609 | ```java 610 | // Copyright (C) 2020 by mrBug, Inc. All rights reserved. 611 | ``` 612 | 613 | 614 | #### Informative Comments 615 | 616 | 617 | Good: 618 | ```java 619 | // format matched kk:mm:ss EEE, MMM dd, yyyy 620 | Pattern timeMatcher = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*"); 621 | ``` 622 | 623 | 624 | #### Explanation of Intent 625 | 626 | Bad: 627 | ```java 628 | int isBetter(string human){ 629 | if(human == "woman") 630 | return 0; 631 | } 632 | ``` 633 | 634 | Good: 635 | ```java 636 | int isBetter(string human){ 637 | if(human == "woman") 638 | return 0; // i mean woman is not better 639 | } 640 | ``` 641 | 642 | 643 | #### Warning of concequences 644 | 645 | Bad: 646 | ```java 647 | void relaxFunction(){ 648 | system.sleep(1000000) 649 | } 650 | ``` 651 | 652 | Good: 653 | ```java 654 | // Don't run unless you have some time 655 | void relaxFunction(){ 656 | system.sleep(1000000) 657 | } 658 | ``` 659 | 660 | 661 | #### TODO Comments 662 | 663 | Bad: 664 | ```java 665 | void makeSomeDangerInSoftware(){ 666 | 667 | } 668 | ``` 669 | 670 | Good: 671 | ```java 672 | // TODO write this function when company doesn't pay the money 673 | void makeSomeDangerInSoftware(){ 674 | 675 | } 676 | ``` 677 | 678 | 679 | ### Bad Comments 680 | 681 | #### Redundant Comments 682 | 683 | Bad: 684 | ```java 685 | // this function input is name of user and check if the name is you return false becouse you 686 | // are not cool enough 687 | bool isCoolPerson(string name){ 688 | if(name == "you") 689 | return false; 690 | } 691 | ``` 692 | 693 | Good: 694 | ```java 695 | bool isCoolPerson(string name){ 696 | if(name == "you") 697 | return false; 698 | } 699 | ``` 700 | 701 | 702 | #### Mandated Comments 703 | 704 | Bad: 705 | ```java 706 | /** 707 | * The Manager implementation with which this Container is * associated. 708 | */ 709 | protected Manager manager = null; 710 | /** 711 | * The cluster with which this Container is associated. */ 712 | protected Cluster cluster = null; 713 | /** 714 | * The human-readable name of this Container. */ 715 | protected String name = null; 716 | /** 717 | * The parent Container to which this Container is a child. */ 718 | protected Container parent = null; 719 | /** 720 | * The parent class loader to be configured when we install a * Loader. 721 | */ 722 | protected ClassLoader parentClassLoader = null; 723 | 724 | ``` 725 | 726 | Good: 727 | ```java 728 | 729 | protected Manager manager = null; 730 | 731 | protected Cluster cluster = null; 732 | 733 | protected String name = null; 734 | 735 | protected Container parent = null; 736 | 737 | protected ClassLoader parentClassLoader = null; 738 | 739 | ``` 740 | 741 | 742 | #### Journal Comments 743 | 744 | Bad: 745 | ```java 746 | * Changes (from 20-Oct-2020) 747 | * -------------------------- 748 | * fixed and report 749 | 750 | ``` 751 | 752 | Good: 753 | ```java 754 | // keep it clean 755 | ``` 756 | 757 | 758 | #### Noise Comments 759 | 760 | Bad: 761 | ```java 762 | /** The day of the month. */ 763 | private int dayOfMonth; 764 | ``` 765 | 766 | Good: 767 | ```java 768 | private int dayOfMonth; 769 | ``` 770 | 771 | 772 | #### Don’t Use a Comment When You Can Use a Function or a Variable 773 | 774 | Bad: 775 | ```java 776 | // this function gain the pain 777 | // long long description 778 | void a(){ 779 | ... 780 | } 781 | ``` 782 | 783 | Good: 784 | ```java 785 | void painGainer(){ 786 | ... 787 | } 788 | ``` 789 | 790 | 791 | #### Position Markers 792 | 793 | Bad: 794 | ```java 795 | /////////////////////////////////////////// 796 | ///////////////// mrBug /////////////////// 797 | /////////////////////////////////////////// 798 | ``` 799 | 800 | Good: 801 | ```java 802 | // mrBug 803 | ``` 804 | 805 | 806 | #### Closing Brace Comments 807 | 808 | Bad: 809 | ```java 810 | while (deadLine != null) { 811 | 812 | work++; 813 | if(life == null){ 814 | ... 815 | ... 816 | ... 817 | ... 818 | if(time == "night" ){ 819 | ... 820 | ... 821 | ... 822 | ... 823 | ... 824 | ... 825 | ... 826 | } // if : time == night 827 | ... 828 | ... 829 | ... 830 | break; 831 | }// if : life == null 832 | 833 | } //while deadLine 834 | ``` 835 | 836 | Good: 837 | ```java 838 | while (deadLine != null) { 839 | 840 | work++; 841 | if(life == null){ 842 | anotherSimpleFunction() 843 | ... 844 | break; 845 | } 846 | 847 | } 848 | ``` 849 | 850 | 851 | #### Attributions and Bylines 852 | 853 | Bad: 854 | ```java 855 | /* Added by MrBug */ 856 | ``` 857 | 858 | Good: 859 | ```java 860 | // keep it clean, let version-contrlol do ! 861 | ``` 862 | 863 | 864 | #### Commented-Out Code 865 | 866 | Bad: 867 | ```java 868 | this.bytePos = writeBytes(pngIdBytes, 0); 869 | //hdrPos = bytePos; 870 | writeHeader(); writeResolution(); 871 | //dataPos = bytePos; 872 | if (writeImageData()) { 873 | writeEnd(); 874 | // this.pngBytes = resizeByteArray(this.pngBytes, this.maxPos); 875 | 876 | } 877 | ``` 878 | 879 | Good: 880 | ```java 881 | this.bytePos = writeBytes(pngIdBytes, 0); 882 | writeHeader(); writeResolution(); 883 | if (writeImageData()) { 884 | writeEnd(); 885 | } 886 | ``` 887 | 888 | 889 | #### Nonlocal Information 890 | 891 | Bad: 892 | ```java 893 | /** 894 | * nonLocalFunction description 895 | * nonLocalFunction description 896 | * nonLocalFunction description 897 | * nonLocalFunction description 898 | * nonLocalFunction description 899 | * nonLocalFunction description 900 | * 901 | * 902 | * 903 | * Port on which fitnesse would run. * 904 | * @param fitnessePort 905 | */ 906 | public void setFitnessePort(int fitnessePort) { 907 | this.fitnessePort = fitnessePort; 908 | } 909 | 910 | ``` 911 | 912 | Good: 913 | ```java 914 | /** 915 | * Port on which fitnesse would run. * 916 | * @param fitnessePort 917 | */ 918 | public void setFitnessePort(int fitnessePort) { 919 | this.fitnessePort = fitnessePort; 920 | } 921 | ``` 922 | 923 | 924 | #### Too Much Information 925 | 926 | Bad: 927 | ```java 928 | /* 929 | RFC 2045 - Multipurpose Internet Mail Extensions (MIME) 930 | Part One: Format of Internet Message Bodies 931 | section 6.8. Base64 Content-Transfer-Encoding 932 | The encoding process represents 24-bit groups of input bits as output strings of 4 encoded characters. Proceeding from left to right, a 24-bit input group is formed by concatenating 3 8-bit input groups. These 24 bits are then treated as 4 concatenated 6-bit groups, each of which is translated into a single digit in the base64 alphabet. When encoding a bit stream via the base64 encoding, the bit stream must be presumed to be ordered with the most-significant-bit first. That is, the first bit in the stream will be the high-order bit in the first 8-bit byte, and the eighth bit will be the low-order bit in the first 8-bit byte, and so on. 933 | */ 934 | ``` 935 | 936 | Good: 937 | ```java 938 | // keep it clean 939 | ``` 940 | 941 | # Formatting 942 | 943 | ### Vertical Formatting 944 | 945 | Bad: 946 | ```java 947 | lineOfCodePerFile > 500 948 | ``` 949 | 950 | Good: 951 | ```java 952 | lineOfCodePerFile < 100 953 | ``` 954 | 955 | 956 | ### The Newspaper Metaphor 957 | 958 | Bad: 959 | ```java 960 | 961 | ``` 962 | 963 | Good: 964 | ```java 965 | 966 | ... 967 | 968 | ... 969 |
970 | ``` 971 | 972 | 973 | ### Vertical Openness Between Concepts 974 | 975 | Bad: 976 | ```java 977 | package fitnesse.wikitext.widgets; 978 | import java.util.regex.*; 979 | public class BoldWidget extends ParentWidget { 980 | public static final String REGEXP = "'''.+?'''"; 981 | private static final Pattern pattern = Pattern.compile("'''(.+?)'''", 982 | Pattern.MULTILINE + Pattern.DOTALL); 983 | public BoldWidget(ParentWidget parent, String text) throws Exception { 984 | super(parent); 985 | Matcher match = pattern.matcher(text); 986 | match.find(); 987 | addChildWidgets(match.group(1)); 988 | } 989 | public String render() throws Exception { 990 | StringBuffer html = new StringBuffer(""); 991 | html.append(childHtml()).append(""); 992 | return html.toString(); 993 | } 994 | } 995 | ``` 996 | 997 | Good: 998 | ```java 999 | package fitnesse.wikitext.widgets; 1000 | 1001 | import java.util.regex.*; 1002 | 1003 | public class BoldWidget extends ParentWidget { 1004 | public static final String REGEXP = "'''.+?'''"; 1005 | private static final Pattern pattern = Pattern.compile("'''(.+?)'''", 1006 | Pattern.MULTILINE + Pattern.DOTALL 1007 | ); 1008 | 1009 | public BoldWidget(ParentWidget parent, String text) throws Exception { 1010 | super(parent); 1011 | Matcher match = pattern.matcher(text); 1012 | match.find(); 1013 | addChildWidgets(match.group(1)); 1014 | } 1015 | 1016 | public String render() throws Exception { 1017 | StringBuffer html = new StringBuffer(""); 1018 | html.append(childHtml()).append(""); 1019 | return html.toString(); 1020 | } 1021 | } 1022 | ``` 1023 | 1024 | 1025 | ### Vertical Distance 1026 | 1027 | Bad: 1028 | ```java 1029 | int verifyAuthentication(string userId){ 1030 | for ... 1031 | if ... 1032 | int password; 1033 | for ... 1034 | if ... 1035 | ... 1036 | ... 1037 | // long distance ! 1038 | password = 1234; 1039 | } 1040 | ``` 1041 | 1042 | Good: 1043 | ```java 1044 | // declare first of function and/or class 1045 | // because its small 1046 | int verifyAuthentication(string userId){ 1047 | int password; 1048 | for ... 1049 | if ... 1050 | password = 1234; 1051 | } 1052 | ``` 1053 | 1054 | 1055 | ### Dependent Functions 1056 | 1057 | Bad: 1058 | ```java 1059 | bool coolFunction(string nothing){ 1060 | if(nothing.isEqual(null)) 1061 | return true; 1062 | 1063 | return false; 1064 | } 1065 | ... 1066 | /* long long story of code */ 1067 | ... 1068 | coolFunction("haha") 1069 | ``` 1070 | 1071 | Good: 1072 | ```java 1073 | bool coolFunction(string nothing){ 1074 | if(nothing.isEqual(null)) 1075 | return true; 1076 | 1077 | return false; 1078 | } 1079 | coolFunction("haha") 1080 | ... 1081 | /* long long story of code */ 1082 | ``` 1083 | 1084 | 1085 | ### Horizontal Formatting 1086 | 1087 | Bad: 1088 | ```java 1089 | public static double calculateArea(double vertical,double horizental) { 1090 | double determinant=determinant(vertical,horizental); 1091 | return (-b+Math.sqrt(determinant))/(2*a); 1092 | } 1093 | ``` 1094 | 1095 | Good: 1096 | ```java 1097 | public static double calculateArea(double vertical, double horizental) { 1098 | double determinant = determinant(vertical, horizental); 1099 | return (-horizental + Math.sqrt(determinant)) / (2*a); 1100 | } 1101 | ``` 1102 | 1103 | 1104 | ### Indentation 1105 | 1106 | Bad: 1107 | ```java 1108 | public class FitNesseServer implements SocketServer { private FitNesseContext 1109 | context; public FitNesseServer(FitNesseContext context) { this.context = 1110 | context; } public void serve(Socket s) { serve(s, 10000); } public void 1111 | serve(Socket s, long requestTimeout) { try { FitNesseExpediter sender = new 1112 | FitNesseExpediter(s, context); 1113 | sender.setRequestParsingTimeLimit(requestTimeout); sender.start(); } 1114 | catch(Exception e) { e.printStackTrace(); } } } 1115 | ``` 1116 | 1117 | Good: 1118 | ```java 1119 | public class FitNesseServer implements SocketServer { 1120 | private FitNesseContext context; 1121 | this.context = context; 1122 | } 1123 | public void serve(Socket s) { 1124 | serve(s, 10000); 1125 | } 1126 | public void serve(Socket s, long requestTimeout) { 1127 | try { 1128 | FitNesseExpediter sender = new FitNesseExpediter(s, context); 1129 | sender.setRequestParsingTimeLimit(requestTimeout); 1130 | sender.start(); 1131 | } 1132 | catch (Exception e) { 1133 | e.printStackTrace(); 1134 | } 1135 | } 1136 | } 1137 | ``` 1138 | 1139 | 1140 | ### Team Rules 1141 | 1142 | Bad: 1143 | ```java 1144 | Every programmer has his own favorite formatting rules 1145 | ``` 1146 | 1147 | Good: 1148 | ```java 1149 | A team of developers should agree 1150 | upon a single formatting style 1151 | ``` 1152 | 1153 | # Error Handeling 1154 | ### Use Exceptions Rather Than Return Codes 1155 | 1156 | Bad: 1157 | ```java 1158 | errno value Error 1159 | 1 /* Operation not permitted */ 1160 | 2 /* No such file or directory */ 1161 | 3 /* No such process */ 1162 | 4 /* Interrupted system call */ 1163 | 5 /* I/O error */ 1164 | 6 /* No such device or address */ 1165 | 7 /* Argument list too long */ 1166 | 8 /* Exec format error */ 1167 | 9 /* Bad file number */ 1168 | 10 /* No child processes */ 1169 | 11 /* Try again */ 1170 | 12 /* Out of memory */ 1171 | 13 /* Permission denied */ 1172 | ``` 1173 | 1174 | Good: 1175 | ```java 1176 | public void sendShutDown() { 1177 | try { 1178 | tryToShutDown(); 1179 | } catch (DeviceShutDownError e) { 1180 | logger.log(e); 1181 | } 1182 | private void tryToShutDown() throws DeviceShutDownError { 1183 | // .. 1184 | } 1185 | ``` 1186 | 1187 | ### Write Your Try-Catch-Finally Statement First 1188 | 1189 | Bad: 1190 | ```java 1191 | int main(){ 1192 | // code 1193 | } 1194 | ``` 1195 | 1196 | Good: 1197 | ```java 1198 | try { 1199 | int main(){ 1200 | // code 1201 | 1202 | } 1203 | } catch (Error e) { 1204 | logger.log(e); 1205 | } 1206 | 1207 | ``` 1208 | 1209 | ### Provide Context with Exceptions 1210 | 1211 | Bad: 1212 | ```java 1213 | public void sendShutDown() { 1214 | try { 1215 | tryToShutDown(); 1216 | } catch (DeviceShutDownError e) { 1217 | logger.log("some error occured !"); 1218 | } 1219 | private void tryToShutDown() throws DeviceShutDownError { 1220 | // .. 1221 | } 1222 | ``` 1223 | 1224 | Good: 1225 | ```java 1226 | public void sendShutDown() { 1227 | try { 1228 | tryToShutDown(); 1229 | } catch (DeviceShutDownError e) { 1230 | logger.log(class.name + function.name + e); 1231 | } 1232 | private void tryToShutDown() throws DeviceShutDownError { 1233 | // .. 1234 | } 1235 | ``` 1236 | 1237 | ### Don’t Return Null 1238 | 1239 | Bad: 1240 | ```java 1241 | List employees = getEmployees(); 1242 | if (employees != null) { 1243 | for(Employee e : employees) { 1244 | totalPay += e.getPay(); 1245 | } 1246 | } 1247 | 1248 | Good: 1249 | ```java 1250 | public List getEmployees() { 1251 | if( .. there are no employees .. ) 1252 | return Collections.emptyList(); 1253 | // or throw an exception 1254 | } 1255 | ``` 1256 | 1257 | ### Don’t Pass Null 1258 | 1259 | Bad: 1260 | ```java 1261 | public class MetricsCalculator 1262 | { 1263 | public double xProjection(Point p1, Point p2) { 1264 | return (p2.x – p1.x) * 1.5; 1265 | } 1266 | … 1267 | } 1268 | ``` 1269 | 1270 | Good: 1271 | ```java 1272 | public class MetricsCalculator 1273 | { 1274 | public double xProjection(Point p1, Point p2) { 1275 | assert p1 != null : "p1 should not be null"; 1276 | assert p2 != null : "p2 should not be null"; 1277 | return (p2.x – p1.x) * 1.5; 1278 | } 1279 | } 1280 | ``` 1281 | 1282 | # Boundaries 1283 | ### Using Third-Party Code 1284 | 1285 | Bad: 1286 | ```java 1287 | Map sensors = new HashMap(); 1288 | Sensor s = (Sensor)sensors.get(sensorId); 1289 | ``` 1290 | 1291 | Good: 1292 | ```java 1293 | public class Sensors { 1294 | private Map sensors = new HashMap(); 1295 | 1296 | public Sensor getById(String id) { 1297 | return (Sensor) sensors.get(id); 1298 | } 1299 | 1300 | } 1301 | ``` 1302 | 1303 | ### Exploring and Learning Boundaries 1304 | 1305 | Bad: 1306 | ```java 1307 | import superComputer 1308 | 1309 | superComputer.solve() 1310 | ``` 1311 | 1312 | Good: 1313 | ```java 1314 | import superComputer 1315 | 1316 | @test 1317 | ... 1318 | @test 1319 | ... 1320 | 1321 | ``` 1322 | 1323 | 1324 | ### Using Code That Does Not Yet Exist 1325 | 1326 | Bad: 1327 | ```java 1328 | // stop development 1329 | ``` 1330 | 1331 | Good: 1332 | ```java 1333 | interface pendingModule() 1334 | interface fakeApi() 1335 | 1336 | ``` 1337 | 1338 | ### Clean Boundaries 1339 | 1340 | Bad: 1341 | ```java 1342 | if (third-party == 'changed') 1343 | change(wholeSoftware) 1344 | ``` 1345 | 1346 | Good: 1347 | ```java 1348 | if (third-party == 'changed') 1349 | change(surviveSoftwareWithoutBigChange) 1350 | ``` 1351 | 1352 | # Unit Tests 1353 | ### The Three Laws of TDD 1354 | 1355 | Bad: 1356 | ```java 1357 | write test and write test 1358 | ``` 1359 | 1360 | Good: 1361 | ```java 1362 | unitTest() -> failedUnitTest() -> makeCodeBetter() -> isUnitTestFail() -> production() -> makeMoney() 1363 | ``` 1364 | 1365 | ### Clean Tests 1366 | 1367 | Bad: 1368 | ```java 1369 | // just write test, and lose them 1370 | ``` 1371 | 1372 | Good: 1373 | ```java 1374 | // keep it clean, make it readable! 1375 | ``` 1376 | 1377 | ### One Assert per test 1378 | 1379 | Bad: 1380 | ```java 1381 | @Test 1382 | testPainMaximization(){ 1383 | if(lifeExistance) 1384 | return true; 1385 | 1386 | if(isDeveloper) 1387 | return true; 1388 | 1389 | } 1390 | ``` 1391 | 1392 | Good: 1393 | ```java 1394 | @Test 1395 | testLifeExistance() 1396 | 1397 | @Test 1398 | testIsDeveloper() 1399 | 1400 | ``` 1401 | 1402 | ### F.I.R.S.T 1403 | 1404 | 1405 | Good: 1406 | ```java 1407 | Fast() 1408 | Independient() 1409 | Repeatable() // any env 1410 | SelfValidating() // return true || false 1411 | Timely() // write befor production 1412 | ``` 1413 | 1414 | # Classes 1415 | ### Encapsulation 1416 | 1417 | Bad: 1418 | ```java 1419 | // make all things private 1420 | // fanatic about encapsulation 1421 | ``` 1422 | 1423 | Good: 1424 | ```java 1425 | // make protected something 1426 | // access by a test 1427 | ``` 1428 | 1429 | ### Classes Should be Small 1430 | 1431 | Bad: 1432 | ```java 1433 | public class longBoringStory(){ 1434 | 1435 | ... 1436 | too many methods 1437 | .... 1438 | } 1439 | ``` 1440 | 1441 | Good: 1442 | ```java 1443 | public class shortCoolStory(){ 1444 | 1445 | public void tellMeStory(){ 1446 | 1447 | } 1448 | } 1449 | ``` 1450 | 1451 | ### The Single Responsibility Principle 1452 | 1453 | Bad: 1454 | ```java 1455 | public class SuperSuperResponsibility(){ 1456 | 1457 | ... 1458 | too many methods 1459 | .... 1460 | } 1461 | ``` 1462 | 1463 | Good: 1464 | ```java 1465 | public class cleanSingleResponsibility(){ 1466 | 1467 | public void iAmCleanYouKnow(){ 1468 | 1469 | } 1470 | } 1471 | ``` 1472 | 1473 | 1474 | ### Cohesion 1475 | 1476 | Bad: 1477 | ```java 1478 | public class longBoringStory(){ 1479 | 1480 | string story; 1481 | string character; 1482 | string word; 1483 | string page; 1484 | srting time; 1485 | ... 1486 | devilManipulator(string story, string character, 1487 | string word, string page, srting time ){ 1488 | 1489 | } 1490 | } 1491 | ``` 1492 | 1493 | Good: 1494 | ```java 1495 | public class ShortStory(){ 1496 | 1497 | string story; 1498 | string character; 1499 | 1500 | ... 1501 | tellStory(string story, string character ){ 1502 | 1503 | } 1504 | } 1505 | ``` 1506 | 1507 | 1508 | ### Maintaining Cohesion Results in Many Small Classes 1509 | 1510 | Bad: 1511 | ```java 1512 | public class BigClassDoEveryThing(){ 1513 | 1514 | } 1515 | ``` 1516 | 1517 | Good: 1518 | ```java 1519 | public class Cool(){ 1520 | 1521 | } 1522 | public class Small(){ 1523 | 1524 | } 1525 | public class Class(){ 1526 | 1527 | } 1528 | 1529 | ``` 1530 | 1531 | 1532 | ### Organizing for change 1533 | 1534 | Bad: 1535 | ```java 1536 | // no abstraction just implement to work and make money 1537 | ``` 1538 | 1539 | Good: 1540 | ```java 1541 | // use interfaces and design in multiLayer abstraction 1542 | ``` 1543 | 1544 | --------------------------------------------------------------------------------