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

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

toya108
September 28, 2022

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

toya108

September 28, 2022
Tweet

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