121 lines
4.9 KiB
Python
121 lines
4.9 KiB
Python
|
|
"""
|
|||
|
|
Доменные модели инструментария.
|
|||
|
|
Все типы данных, которыми обмениваются модули системы.
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
from dataclasses import dataclass, field
|
|||
|
|
from enum import Enum, auto
|
|||
|
|
from pathlib import Path
|
|||
|
|
from typing import Dict, List, Optional, Set
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
# Метрики
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class FunctionMetrics:
|
|||
|
|
"""Метрики одной функции / метода."""
|
|||
|
|
name: str
|
|||
|
|
file: str
|
|||
|
|
line: int
|
|||
|
|
ccn: int # Цикломатическая сложность (McCabe)
|
|||
|
|
nloc: int # Строк кода без комментариев
|
|||
|
|
params: int # Число параметров
|
|||
|
|
language: str # "cpp" | "js"
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def is_complex(self) -> bool:
|
|||
|
|
return self.ccn > 10
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def is_large(self) -> bool:
|
|||
|
|
return self.nloc > 50
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class ModuleMetrics:
|
|||
|
|
"""Агрегированные метрики модуля (директории или файла)."""
|
|||
|
|
name: str # Имя модуля (путь относительно корня)
|
|||
|
|
files: List[str] = field(default_factory=list)
|
|||
|
|
functions: List[FunctionMetrics] = field(default_factory=list)
|
|||
|
|
language: str = "mixed" # "cpp" | "js" | "mixed"
|
|||
|
|
|
|||
|
|
# Агрегированные показатели (заполняются MetricsEngine)
|
|||
|
|
total_nloc: int = 0
|
|||
|
|
avg_ccn: float = 0.0
|
|||
|
|
max_ccn: int = 0
|
|||
|
|
total_functions: int = 0
|
|||
|
|
heavy_functions_count: int = 0 # Функций с CCN > порога
|
|||
|
|
heavy_functions_ratio: float = 0.0
|
|||
|
|
|
|||
|
|
# Граф зависимостей (заполняется DependencyAnalyzer)
|
|||
|
|
coupling_out: int = 0 # Исходящая связность (fan-out)
|
|||
|
|
coupling_in: int = 0 # Входящая связность (fan-in)
|
|||
|
|
instability: float = 0.0 # C_out / (C_in + C_out), метрика Р. Мартина
|
|||
|
|
has_cycles: bool = False # Участвует в циклической зависимости
|
|||
|
|
|
|||
|
|
# Зависимости
|
|||
|
|
dependencies: Set[str] = field(default_factory=set) # Модули, от которых зависит этот
|
|||
|
|
dependents: Set[str] = field(default_factory=set) # Модули, зависящие от этого
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
# Классификация риска и стратегия
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
class RiskLevel(Enum):
|
|||
|
|
"""Уровень архитектурного риска модуля."""
|
|||
|
|
LOW = "low"
|
|||
|
|
MEDIUM = "medium"
|
|||
|
|
HIGH = "high"
|
|||
|
|
CRITICAL = "critical"
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ModernizationStrategy(Enum):
|
|||
|
|
"""Рекомендуемая стратегия модернизации (Lehman & Belady, 1985)."""
|
|||
|
|
KEEP = auto() # Оставить без изменений
|
|||
|
|
REFACTOR = auto() # Локальный рефакторинг (Fowler, 2018)
|
|||
|
|
REENGINEER = auto() # Реинжиниринг архитектуры
|
|||
|
|
REPLACE = auto() # Полная замена
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class ModuleDecision:
|
|||
|
|
"""Решение DecisionEngine по конкретному модулю."""
|
|||
|
|
module_name: str
|
|||
|
|
risk_level: RiskLevel
|
|||
|
|
strategy: ModernizationStrategy
|
|||
|
|
reasons: List[str] = field(default_factory=list) # Причины в человекочитаемом виде
|
|||
|
|
priority: int = 0 # Приоритет в плане рефакторинга (1 = высший)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
# Итоговый отчёт
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class AnalysisReport:
|
|||
|
|
"""Итоговый отчёт по всей системе."""
|
|||
|
|
project_root: str
|
|||
|
|
modules: Dict[str, ModuleMetrics] = field(default_factory=dict)
|
|||
|
|
decisions: List[ModuleDecision] = field(default_factory=list)
|
|||
|
|
dependency_cycles: List[List[str]] = field(default_factory=list)
|
|||
|
|
|
|||
|
|
# Системные агрегаты
|
|||
|
|
total_files: int = 0
|
|||
|
|
total_nloc: int = 0
|
|||
|
|
total_functions: int = 0
|
|||
|
|
avg_system_ccn: float = 0.0
|
|||
|
|
system_risk_level: RiskLevel = RiskLevel.LOW
|
|||
|
|
recommended_system_strategy: ModernizationStrategy = ModernizationStrategy.KEEP
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def critical_modules(self) -> List[ModuleDecision]:
|
|||
|
|
return [d for d in self.decisions if d.risk_level == RiskLevel.CRITICAL]
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def high_risk_modules(self) -> List[ModuleDecision]:
|
|||
|
|
return [d for d in self.decisions if d.risk_level in (RiskLevel.HIGH, RiskLevel.CRITICAL)]
|