Clean Code With Kotlin 2


前編:https://velog.io/@woga1999/Clean-Code-With-Kotlin-1

エラーコントロール


予期せぬ事態に対処する方法については、次の事項を考慮します.
  • カスタムエラーよりもスタンダードエラーの方が好き.
  • エラー処理も重要ですが、曖昧論理はエラーです.
  • コートリンはunchecked exception達のみ.
  • エラー結果が不足している場合はnullまたは失敗結果を選択してください.
  • また、コトリンでは、関数が何も返されない場合に使用できるNothing
    fun infiniteLoop(): Nothing {
        while (true) {
            println("Hi there!")
        }
    }
    fun throwException(): Nothing {
        throw IllegalStateException()
    }
    エラー処理やステータス管理に適した角速度概念もあります.それは

    sealed class


    公式文書によると
    「Sealedクラスは、制限されたクラス階層を表すために使用されます.値は制限されたセットのタイプの1つであってもよいが、他のタイプではありません.ある意味では、列挙値のセットも制限されますが、各列挙定数は単一のインスタンスとしてのみ存在し、密閉クラスのサブクラスはステータスを含みます.複数のインスタンスがあります."
  • result class使用時
  • sealed class MovieSearchResult
    data class MovieFound(val movie: Movie) : MovieSearchResult()
    object MovieNotFound : MovieSearchResult()
    object DatabaseOffline : MovieSearchResult()
    
    fun displayMovieResult(movieResult: MovieSearchResult) {
        when(movieResult) {
            is MovieFound -> println("yey, we found the movie")
            is MovieNotFound -> TODO()
        }
    }
    val <T> T.exhaustive: T
        get() = this
        
    fun displayMovieResult(movieResult: MovieSearchResult) {
        when(movieResult) {
            is MovieFound -> println("yey, we found the movie")
            is MovieNotFound -> TODO()
        }.exhaustive
    }

    テスト


    テストを清潔に保つ:
  • テストコードは生産コードと同じSame Qualityを使用しているため可読性が重要
  • テストコードは維持・強化すべき유연성, 유지보수성, 재사용 가능성, 가독성
  • テストコードにも読みやすい同じドメイン言語を使用する.
  • 本番コードで行ったように、テストコードを再設計します.
    各テストには、テストに失敗した理由があるため、aseertが含まれている必要があります.
  • Every test function in a JUnit test should have one and only one assert statement.
  • Given、When、Then Naming(BD–行動駆動開発行動主導開発)
  • @Test
        public void givenValidInputsWhenGetRatesCalledThenLiveDataObserverEmitsSuccessState() {
            //given
            String date = "2000-10-10";
            String baseCurrency = "USD";
            Single<ExchangeRateResponse> expectedResponse = Single.just(ExchangeRateResponse.EMPTY);
    
            //when
            when(mockRepository.requestExchangeRates(date, baseCurrency)).thenReturn(expectedResponse);
            testSubject.getRates(date, baseCurrency);
    
            //then
            State result = testSubject.getExchangeRateData().getValue();
            assertThat(result).isEqualTo(new State.Success(ExchangeRateResponse.EMPTY));
    
            verify(mockObserver).onChanged(isA(State.Loading.class));
            verify(mockObserver).onChanged(isA(State.Success.class));
    
            verify(mockObserver, times(1)).onChanged(isA(State.Loading.class));
            verify(mockObserver, times(1)).onChanged(isA(State.Success.class));
    
            verify(mockObserver, never()).onChanged(isA(State.Failure.class));
    
            verifyNoMoreInteractions(mockObserver);
        }
  • The best thing to do is to minimize the number of asserts.
  • Test just one concept per test function.

  • FIRSTの原則


    テストコードを作成するときは、この原則を遵守します.
  • 快速:テストは快速運転が必要
  • 独立:テストは相互に依存してはいけない.自分の順番で運転できるはずです.
  • Repetable:特定の構造を必要とせずに、すべての環境で繰り返し可能であること.
  • 自己検証:Boolean出力(true/false)が必要です.
  • Timely:オペレーティングコードを作成する前に決定を下す.
  • コメント


    エラーコードのコメントを追加しないで、コードとして解釈されるまで書き直します.

    example

  • no!! comment!! like this!!
  • class Log {
    	/** The data. */
        var data: String = ""
        
        /** The number of data. */
        var numberOfData = 0
        
        /**
        * Secondary constructor.
        */
        constructor(data: String){
        }
    }
    fun updataUserPoints(user: User, points: Int) {
    	try {
        	user.points += points
        } //try
        catch (e: java.lang.Exception) {
        	//User not found
            println("user not found")
        } //catch
     }
  • need comments
  • <Legal Comments>
    /**
    Coptyright (c) <year> <copyrigth holders>
    
    Permission is hereby granted, blah blah
    */
    <Warning of consequences>
    fun makeStandardDateFormant(): SimpleDateFormat? {
    	//SimpleDateFormat is not thread safe,
        //so we need to create each instance independently.
        val dateFormat = SimpleDateFormat("EEE, dd MMM yyyy HH:mm: ss z")
        dateFormat.timeZone = TimeZone.getTimeZone("GMT")
        return dateFormat
    +)
  • bad
  • // Check to see if the employee is eligible for full benefits
    if (employee.rate.equalsIgnoreCase("hours") &&
    employee.objectiveDone > 3)
  • good
  • if (employee.isEligibleForFullBenefits())

    Reference


    https://magdamiu.com/2021/08/23/clean-code-with-kotlin-2/