Skip to main content

cha_core/plugins/
middle_man.rs

1use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
2
3/// Detect classes where most methods only delegate to another object.
4pub struct MiddleManAnalyzer {
5    pub min_methods: usize,
6    pub delegation_ratio: f64,
7}
8
9impl Default for MiddleManAnalyzer {
10    fn default() -> Self {
11        Self {
12            min_methods: 3,
13            delegation_ratio: 0.5,
14        }
15    }
16}
17
18impl Plugin for MiddleManAnalyzer {
19    fn name(&self) -> &str {
20        "middle_man"
21    }
22
23    fn description(&self) -> &str {
24        "Class that only delegates to others"
25    }
26
27    fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
28        ctx.model
29            .classes
30            .iter()
31            .filter(|c| {
32                c.method_count >= self.min_methods
33                    && c.delegating_method_count > 0
34                    && (c.delegating_method_count as f64 / c.method_count as f64)
35                        >= self.delegation_ratio
36            })
37            .map(|c| Finding {
38                smell_name: "middle_man".into(),
39                category: SmellCategory::Couplers,
40                severity: Severity::Hint,
41                location: Location {
42                    path: ctx.file.path.clone(),
43                    start_line: c.start_line,
44                    end_line: c.end_line,
45                    name: Some(c.name.clone()),
46                },
47                message: format!(
48                    "Class `{}` delegates {}/{} methods, acting as a middle man",
49                    c.name, c.delegating_method_count, c.method_count
50                ),
51                suggested_refactorings: vec!["Remove Middle Man".into()],
52                actual_value: Some(c.delegating_method_count as f64 / c.method_count as f64),
53                threshold: Some(self.delegation_ratio),
54            })
55            .collect()
56    }
57}