Metastatic.Analysis.Encapsulation (Metastatic v0.10.4)

View Source

Encapsulation analysis for containers (modules/classes).

Detects violations of encapsulation principles - the practice of hiding internal state and requiring all interaction to go through well-defined interfaces.

Principles Checked

  1. Information Hiding: Internal state should be private
  2. Controlled Access: State access should go through methods
  3. Interface Segregation: Public interface should be minimal and focused
  4. Implementation Hiding: Internal details should not leak

Detected Violations

Public State (Critical)

Instance variables that are publicly accessible without getters/setters. Breaks encapsulation by allowing uncontrolled state modification.

Missing Accessors (High)

Direct state access/modification without using accessor methods. Bypasses potential validation and business logic.

Excessive Accessors (Medium)

Too many getter/setter pairs relative to behavior methods. May indicate a "data class" that lacks encapsulation of behavior.

Leaky Abstraction (Medium)

Methods that expose internal implementation details. Makes refactoring difficult and couples clients to implementation.

Examples

# Good encapsulation
ast = {:container, :class, "BankAccount", %{}, [
  {:function_def, :public, "deposit", ["amount"], %{},
   {:augmented_assignment, :+,
    {:attribute_access, {:variable, "self"}, "balance"}, {:variable, "amount"}}},
  {:function_def, :public, "get_balance", [], %{},
   {:attribute_access, {:variable, "self"}, "balance"}}
]}

doc = Document.new(ast, :python)
{:ok, result} = Encapsulation.analyze(doc)

result.violations  # => []
result.score       # => 100
result.assessment  # => :excellent

# Poor encapsulation - direct state access
ast = {:container, :class, "User", %{}, [
  {:function_def, :public, "process", [], %{},
   {:assignment, {:attribute_access, {:variable, "self"}, "status"}, {:literal, :string, "done"}}}
]}

{:ok, result} = Encapsulation.analyze(doc)
result.violations  # => [%{type: :missing_accessor, ...}]
result.assessment  # => :poor

Summary

Functions

Analyze encapsulation of a container (module/class/namespace).

Analyze encapsulation of a container (module/class/namespace).

Functions

analyze(language_or_doc, source_or_ast_or_opts \\ [], opts \\ [])

@spec analyze(Metastatic.language(), term(), keyword()) ::
  {:ok, map()} | {:error, term()}

Analyze encapsulation of a container (module/class/namespace).

Returns {:ok, result} if the AST contains a container, or {:error, reason} otherwise.

Examples

iex> ast = {:container, :class, "Example", %{}, [
...>   {:function_def, :public, "get_value", [], %{},
...>    {:attribute_access, {:variable, "self"}, "value"}}
...> ]}
iex> doc = Metastatic.Document.new(ast, :python)
iex> {:ok, result} = Metastatic.Analysis.Encapsulation.analyze(doc)
iex> result.score
100
iex> result.assessment
:excellent

analyze!(language_or_doc, source_or_ast_or_opts \\ [], opts \\ [])

@spec analyze!(Metastatic.language(), term(), keyword()) :: map()

Analyze encapsulation of a container (module/class/namespace).

Returns {:ok, result} if the AST contains a container, or {:error, reason} otherwise.

Examples

iex> ast = {:container, :class, "Example", %{}, [
...>   {:function_def, :public, "get_value", [], %{},
...>    {:attribute_access, {:variable, "self"}, "value"}}
...> ]}
iex> doc = Metastatic.Document.new(ast, :python)
iex> {:ok, result} = Metastatic.Analysis.Encapsulation.analyze(doc)
iex> result.score
100
iex> result.assessment
:excellent

Unlike not-banged version, this one either returns a result or raises