Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Myth of Clean Code

Aung Kyaw Paing
December 08, 2024
54

The Myth of Clean Code

Aung Kyaw Paing

December 08, 2024
Tweet

Transcript

  1. 5 “fault fixing in source code with abbreviated identifiers needs

    a more methodical approach and abbreviated identifiers are not perceived as major obstacle to fix faults in source code.”
  2. 7 “Every function in this program was just two, or

    three, or four lines long. Each was transparently obvious… That’s how short your functions should be!” - Kent Beck
  3. 8 fun fetchProfile() { val response = fetch("my_api_url") if (response.code

    == 200) { return processSuccess(response) } else { processError(response) } }
  4. 9 fun fetchProfile() { val response = fetch("my_api_url") if (response.code

    == 200) { val responseBody = JSONObject(response.body()) return Profile( name = responseBody["name"], age = responseBody["age"] ) } else { log("fetch fails with responseCode: ${response.code}) Snackbar.show("Fail to get profile data") } }
  5. 10 public static String renderPageWithSetupsAndTeardowns( PageData pageData,boolean isSuite ) throws

    Exception { this.isSuite = isSuite if (isTestPage(pageData)) includeSetupAndTeardownPages(); return pageData.getHtml(); } Actual example from “Clean Code” Book
  6. 11

  7. 12

  8. Classitis 13 FileInputStream fileIputStream = new FileInputStream(fileName); try { Reader

    reader = new BufferedReader(new InputStreamReader(stream, cs)); StringBuilder builder = new StringBuilder(); char[] buffer = new char[8192]; int read; while ((read = reader.read(buffer, 0, buffer.length)) > 0) { builder.append(buffer, 0, read); } return builder.toString(); } finally { stream.close(); }
  9. 16

  10. Shallow Functions 18 public static String renderPageWithSetupsAndTeardowns( PageData pageData,boolean isSuite

    ) throws Exception { includeSetupAndTeardownPages if (isTestPage(pageData)) includeSetupAndTeardownPages(pageData, isSuite); return pageData.getHtml(); } private static isTestPage(pageData: PageData) { return pageData.hasAttribute("Test") } Actual example from “Clean Code” Book
  11. 19 fun fetchProfile() { val response = fetch("my_api_url") if (response.code

    == 200) { val responseBody = JSONObject(response.body()) return Profile( name = responseBody["name"], age = responseBody["age"] ) } else { Log("fetch fails with responseCode: ${response.code}) Snackbar.show("Fail to get profile data") } } Deep Functions
  12. 24 fun log( level: Level, message: String ) { if

    (level.isCritical()) { Logger.error(message) } else { Logger.warn(message) } }
  13. 25 fun log( level: Level, message: String, throwable: Throwable? )

    { if (level.isCritical()) { Logger.error(throwable, message) } else { Logger.warn(message) } } log(Level.Info, "message", Exception("This is not used"))
  14. 27 class AdobeAnalyticsPlugin implements IAnaylticsPlugin { trackAction(values: Map<string, any>) =

    Adobe.track(...) trackEvent(values: Map<string, any>) = Adobe.track(...) } class FirebaseAnalyticsPlugin implements IAnaylticsPlugin { trackAction(values: Map<string, any>) = { /*DO NOTHING*/ } trackEvent(values: Map<string, any>) = Firebase.log(...) }
  15. 29 class AdobeAnalytics { trackAction(values: Map<string, any>) = Adobe.track(...) trackEvent(values:

    Map<string, any>) = Adobe.track(...) } class FirebaseAnalytics { logEvent(values: Map<string, any>) = Firebase.log(values) }
  16. 30 private val adobeAnalytics = new AdobeAnalytics() private val firebaseAnalytics

    = new FirebaseAnalytics() fun trackAction(...) { adobeAnalytics.trackAction(values) } fun trackEvent(...) { adobeAnalytics.trackAction(values) firebaseAnalytics.logEvent(values) }
  17. 34 private WikiPage findInheritedPage(String pageName) throws Exception { return PageCrawlerImpl.getInheritedPage(pageName,

    testPage); } Actual example from “Clean Code” Book! again! Pass-through functions/variables
  18. 35 // On UI SearchScreenViewModel.searchUser("username") // In ViewModel SearchUseCase.execute("username") //

    In SearchUseCase SearchRepository.searchUser("username") // In SearchRepository SearchNetworkDataSource.searchUser("username") // In SearchNetworkDataSource executeApiCall("https://api/search?query=$username") Pass-through functions/variables
  19. 37 class ApplicationInstallCheckViewModel( val appInstance: AppInstance ) { fun isAppInstalled(appId:

    String) { return appInstance.searchAppWithId(appId) != null } } Pass-through functions/variables
  20. 38 class AppInstance { private var instance : AppInstance? static

    fun getInstance() : AppInstance { if (instance == null) { instance = ... } return instance!! } fun isAppInstalled(appId: String) = searchAppWithId(appId) != null } Singletons
  21. 39 val textStyles = getTextStyles() FlightCard(textStyles) { TitleText(textStyles) { Text(textStyles.title)

    } BuyButton(textStyles) { Text(textStyles.button) } } Context Objects
  22. 42 “The ideal number of arguments for a function is

    zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification—and then shouldn’t be used anyway.”
  23. 43 @Composable fun Text( text: String, modifier: Modifier = Modifier,

    color: Color = Color.Unspecified, fontSize: TextUnit = TextUnit.Unspecified, fontStyle: FontStyle? = null, fontWeight: FontWeight? = null, fontFamily: FontFamily? = null, letterSpacing: TextUnit = TextUnit.Unspecified, textDecoration: TextDecoration? = null, textAlign: TextAlign? = null, lineHeight: TextUnit = TextUnit.Unspecified, overflow: TextOverflow = TextOverflow.Clip, softWrap: Boolean = true, maxLines: Int = Int.MAX_VALUE, minLines: Int = 1, onTextLayout: ((TextLayoutResult) -> Unit)? = null, style: TextStyle = LocalTextStyle.current ) { ... }
  24. 44 @Composable fun Text( text: String, modifier: Modifier = Modifier,

    color: Color = Color.Unspecified, fontSize: TextUnit = TextUnit.Unspecified, fontStyle: FontStyle? = null, fontWeight: FontWeight? = null, fontFamily: FontFamily? = null, letterSpacing: TextUnit = TextUnit.Unspecified, textDecoration: TextDecoration? = null, textAlign: TextAlign? = null, lineHeight: TextUnit = TextUnit.Unspecified, overflow: TextOverflow = TextOverflow.Clip, softWrap: Boolean = true, maxLines: Int = Int.MAX_VALUE, minLines: Int = 1, onTextLayout: ((TextLayoutResult) -> Unit)? = null, style: TextStyle = LocalTextStyle.current ) { ... }
  25. Slope-interface API design 46 “Providing multiple APIs at different levels

    of abstraction allows solving 80% of use cases with a simple API, then 80% of the remaining 20% with a more detailed API, and then the final slice with a low-level API. Each layer is built on top of the next one with a measured reduction in API complexity.” https://jakewharton.com/slope-intercept-library-design/
  26. 49

  27. 50

  28. 52 protected static int[] generate(int n) { primes = new

    int[n]; multiplesOfPrimeFactors = new ArrayList<Integer>(); set2AsFirstPrime(); checkOddNumbersForSubsequentPrimes(); return primes; }
  29. 53 fun fetchProfile() { val response = fetch("my_api_url") if (response.code

    == 200) { return processSuccess(response) } else { processError(response) } }
  30. 54 fun fetchProfile() { val response = fetch("my_api_url") if (response.code

    == 200) { val responseBody = JSONObject(response.body()) return Profile( name = responseBody["name"], age = responseBody["age"] ) } else { log("fetch fails with responseCode: ${response.code}) Snackbar.show("Fail to get profile data") } }