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

MaintainabilityIndexを 計測しながらコードの保守性を 改善している話

Avatar for toya108 toya108
September 28, 2022

MaintainabilityIndexを 計測しながらコードの保守性を 改善している話

Avatar for toya108

toya108

September 28, 2022
Tweet

More Decks by toya108

Other Decks in Technology

Transcript

  1. MaintainabilityIndexの計算式 Maintainability Index = MAX(0,(171 - 5.2 * ln(Halstead Volume)

    - 0.23 * (Cyclomatic Complexity) - 16.2 * ln(Lines of Code))*100 / 171) ※詳細は「どうやって MaintainabilityIndexの計測しているか」で説明するよ。 15
  2. • FatViewControllerをVIPERSceneに分割する ◦ 他のコードとアーキテクチャが揃うので読みやすくなる ◦ コードが分割されてMaintainabilityIndexの数値も改善するはず • 元はLegacyの配下にあったが、独立したモジュールに切り出す ◦ 必要なものはCoreに移動し、それ以外はマイレシピのためのモジュールに閉じ込

    める ◦ コードが分割されてMaintainabilityIndexの数値も改善するはず ◦ 副次的にSandboxビルドで動作確認がしやすくなる • SwiftUIにする ◦ TableViewのIndexPath管理から脱却したい ◦ 最近チームでもSwiftUI化を積極的に行っているので リファクタの方針決め 24
  3. 29 • Dartのdocumentでこのように明言されている ◦ https://dartcodemetrics.dev/docs/metrics/maintainability -index • 数値を過信して、コードを読まずに現在の状況を判断するのは良くなさそ う。 (注意)

    MaintainabilityIndexは実験的な 数値なため、過信することも禁物 注: 保守性指標はまだ非常に実験的な指標であり、   他の指標ほど真剣に考慮すべきではありません。
  4. MaintainabilityIndexの計算式 Maintainability Index = MAX(0,(171 - 5.2 * ln(Halstead Volume)

    - 0.23 * (Cyclomatic Complexity) - 16.2 * ln(Lines of Code))*100 / 171) これをSwiftで計算している。 31
  5. swiftでMaintainabilityIndexを計算する let rebasedHalsteadVolume = 5.2 * log(Double(halsteadVolume)) let rebasedCyclomaticComplexity =

    0.23 * Double(cyclomaticComplexity) let rebasedLineOfCode = 16.2 * log(Double(lineOfCode)) let maitainabilityIndex = max(0, CGFloat(171 - rebasedHalsteadVolume - rebasedCyclomaticComplexity - rebasedLineOfCode) * 100 / 171) 32
  6. ①Halstead Volume Maintainability Index = MAX(0,(171 - 5.2 * ln(Halstead

    Volume) - 0.23 * (Cyclomatic Complexity) - 16.2 * ln(Lines of Code))*100 / 171) 33
  7. Halsteadさんが提唱したコードの複雑度を測るための指標の一つ https://dartcodemetrics.dev/docs/metrics/halstead-volume Halstead Volumeとは η1 = the number of distinct

    operators η2 = the number of distinct operands N1 = the total number of operators N2 = the total number of operands Program vocabulary: η=η1+η2 Program length: N=N1+N2 Volume: V=Nlog2η 34
  8. SwiftでHalsteadVolumeを計算する import Accelerate // logの使用に必要 let operators: [String] let distinctOperators:

    Set<String> let operands: [String] let distinctOperands: Set<String> let vocabulary= Float(distinctOperators.count + distinctOperands.count) let length = Float(operators.count + operands.count) let halsteadVolume = length * log2(vocabulary) 36
  9. operatorとoperandの数をカウントする import SwiftSyntax import SwiftSyntaxParser // 計測用のVisitorを用意する class MaintainabilityIndexVisitor: SyntaxVisitor

    { var operators: [String] = [] var distinctOperators: Set<String> { Set(operators) } var operands: [String] = [] var distinctOperands: Set<String> { Set(operands) } … } 39
  10. operatorとoperandの数をカウントする override func visit(_ node: TokenSyntax) -> SyntaxVisitorContinueKind { switch

    node.tokenKind { case .spacedBinaryOperator, .unspacedBinaryOperator, .prefixOperator, .postfixOperator: operators.append(node.text) default: // Add non-reserved words to operands if !node.tokenKind.isKeyword && !node.text.isEmpty { operands.append(node.text) } } } 40
  11. visitorにsourceを食わせる // walk()の実行後にここに入ってくる。 override func visit(_ node: TokenSyntax) -> SyntaxVisitorContinueKind

    { switch node.tokenKind { case .spacedBinaryOperator, .unspacedBinaryOperator, .prefixOperator, .postfixOperator: operators.append(node.text) default: // Add non-reserved words to operands if !node.tokenKind.isKeyword && !node.text.isEmpty { operands.append(node.text) } } } 42
  12. ②Cyclomatic Complexity Maintainability Index = MAX(0,(171 - 5.2 * ln(Halstead

    Volume) - 0.23 * (Cyclomatic Complexity) - 16.2 * ln(Lines of Code))*100 / 171) 43
  13. Cyclomatic Complexityを計算する var cyclomaticComplecity = 1 override func visit(_ node:

    TokenSyntax) -> SyntaxVisitorContinueKind { switch node.tokenKind { case .ifKeyword, .switchKeyword, .forKeyword, .whileKeyword, .guardKeyword, .caseKeyword: cyclomaticComplecity += 1 case .identifier(let identifier): if identifier == "forEach" { cyclomaticComplecity += 1 } … 45
  14. ③Lines of Code Maintainability Index = MAX(0,(171 - 5.2 *

    ln(Halstead Volume) - 0.23 * (Cyclomatic Complexity) - 16.2 * ln(Lines of Code))*100 / 171) 47
  15. try CommandLine.arguments.forEach { argument in let url = URL(fileURLWithPath: argument)

    let fileData = try Data(contentsOf: url) let source = fileData.withUnsafeBytes { buf in return String(decoding: buf.bindMemory(to: UInt8.self), as: UTF8.self) } let lineOfCode = source.split(separator: "\n").count Line of Codeの出し方 • sourceを改行ごとに分割してcountする 49
  16. swiftでMaintainabilityIndexを計算する let rebasedHalsteadVolume = 5.2 * log(Double(halsteadVolume)) let rebasedCyclomaticComplexity =

    0.23 * Double(cyclomaticComplexity) let rebasedLineOfCode = 16.2 * log(Double(lineOfCode)) let maitainabilityIndex = max(0, CGFloat(171 - rebasedHalsteadVolume - rebasedCyclomaticComplexity - rebasedLineOfCode) * 100 / 171) print("\(sourcePath), \(halsteadVolume), \(cyclomaticComplexity), \(lineOfCode), \(maitainabilityIndex)") 50