Architecture Metrics
Loading...
Searching...
No Matches
MetricsCalculator.cs
Go to the documentation of this file.
1using ArchUnitNET.Domain;
2
4
17public static class MetricsCalculator
18{
34 public static IReadOnlyCollection<Component> BuildAssemblyComponents(
35 Architecture architecture,
36 params string[] assemblyNames)
37 {
38 return assemblyNames
39 .Select(assemblyName =>
40 new Component(
41 assemblyName,
42 architecture.Types
43 .Where(t => t.Assembly.Name == assemblyName)
44 .ToList()))
45 .ToList();
46 }
47
68 public static IReadOnlyCollection<ComponentDependencies>
69 BuildDependencyGraph(IEnumerable<Component> components)
70 {
71 var componentMap = components
72 .SelectMany(
73 c => c.Types,
74 (component, type) => (type, component.Name))
75 .ToDictionary(x => x.type, x => x.Name);
76
77 var graph = components.ToDictionary(
78 c => c.Name,
79 _ => new HashSet<string>());
80
81 foreach (var component in components)
82 {
83 foreach (var type in component.Types)
84 {
85 foreach (var dependency in type.Dependencies)
86 {
87 var targetType = dependency.Target;
88
89 if (!componentMap.TryGetValue(
90 targetType,
91 out var targetComponent))
92 {
93 continue;
94 }
95
96 if (targetComponent != component.Name)
97 {
98 graph[component.Name]
99 .Add(targetComponent);
100 }
101 }
102 }
103 }
104
105 return graph
106 .Select(kv => new ComponentDependencies(kv.Key, kv.Value))
107 .ToList();
108 }
109
125 IReadOnlyCollection<ComponentDependencies> graph)
126 {
127 var fanOut = graph.ToDictionary(
128 cd => cd.ComponentName,
129 cd => cd.Dependencies.Count);
130
131 var fanIn = graph.ToDictionary(
132 cd => cd.ComponentName,
133 _ => 0);
134
135 foreach (var componentDep in graph)
136 {
137 foreach (var target in componentDep.Dependencies)
138 {
139 fanIn[target]++;
140 }
141 }
142
143 return new ComponentCouplings(fanIn, fanOut);
144 }
145
181 public static double CalculateAbstractness(Component component)
182 {
183 var totalTypes = component.Types.Count;
184
185 if (totalTypes == 0)
186 return 0;
187
188 var abstractTypes = component.Types.Count(t =>
189 IsAbstractOrExtensible(t));
190
191 return (double)abstractTypes / totalTypes;
192 }
193
208 private static bool IsAbstractOrExtensible(IType type)
209 {
210 // Interfaces son siempre abstractas
211 if (type is Interface)
212 return true;
213
214 // Clases abstractas
215 if (type is Class c && c.IsAbstract == true)
216 return true;
217
218 // Tipos genéricos son extensibles por parametrización
219 if (type.GenericParameters.Count > 0)
220 return true;
221
222 return false;
223 }
224
245 public static IReadOnlyCollection<ComponentMetrics> CalculateMetrics(
246 IReadOnlyCollection<Component> components)
247 {
248 var graph = BuildDependencyGraph(components);
249
250 var couplings = CalculateCouplings(graph);
251
252 return components
253 .Select(component =>
254 {
255 var fanIn = couplings.FanIn[component.Name];
256 var fanOut = couplings.FanOut[component.Name];
257
258 var a = CalculateAbstractness(component);
259
260 var i = fanIn + fanOut == 0 ? 0 : (double)fanOut / (fanIn + fanOut);
261
262 var d = Math.Abs(a + i - 1);
263
264 return new ComponentMetrics(component.Name, fanIn, fanOut, a, i, d);
265 })
266 .ToList();
267 }
268}
Calcula métricas de estabilidad arquitectónica para componentes basadas en sus dependencias.
static IReadOnlyCollection< ComponentMetrics > CalculateMetrics(IReadOnlyCollection< Component > components)
Calcula todas las métricas de estabilidad arquitectónica para los componentes.
static double CalculateAbstractness(Component component)
Calcula la abstracción —A— de una instancia de Component.
static IReadOnlyCollection< ComponentDependencies > BuildDependencyGraph(IEnumerable< Component > components)
Construye un grafo de dependencias entre instancias de Component.
static ComponentCouplings CalculateCouplings(IReadOnlyCollection< ComponentDependencies > graph)
Calcula el acoplamiento aferente -fan-in- y eferente -fan-out- para todos los componentes.
static IReadOnlyCollection< Component > BuildAssemblyComponents(Architecture architecture, params string[] assemblyNames)
Construye una colección de instancias de Component agrupando tipos por ensamblado.
record Component(string Name, IReadOnlyCollection< IType > Types)
Representa un componente arquitectónico, agrupando tipos por ensamblado -assembly-.
record ComponentCouplings(Dictionary< string, int > FanIn, Dictionary< string, int > FanOut)
Contiene el acoplamiento aferente -FanIn- y eferente -FanOut- para todos los componentes.
record ComponentDependencies(string ComponentName, IReadOnlyCollection< string > Dependencies)
Representa las dependencias de un componente hacia otros componentes.
record ComponentMetrics(string Name, int FanIn, int FanOut, double Abstractness, double Instability, double Distance)
Contiene todas las métricas de estabilidad arquitectónica para un componente.