Klasse mit übermäßig tiefer Vererbung
Beschreibung
Klasse mit übermäßig tiefer Vererbung tritt auf, wenn eine Klasse eine Vererbungshierarchie hat, die zu tief ist, was bedeutet, dass sie durch zu viele Elternklassen erweitert wird. CISQ empfiehlt einen maximalen Schwellenwert von 7 Elternklassen. Tiefe Vererbungshierarchien erzeugen fragile Basisklassenprobleme, machen Code schwer zu verstehen und zu warten und verletzen das Prinzip der Komposition über Vererbung. Wenn eine Klasse von vielen Ebenen von Eltern erbt, können Änderungen an jeder Vorfahrenklasse unerwartete Auswirkungen in der gesamten Hierarchie haben.
Risiko
Obwohl hauptsächlich ein Wartbarkeitsproblem, hat tiefe Vererbung indirekte Sicherheitsimplikationen. Komplexe Hierarchien erschweren Sicherheitsaudits, da Verhalten über viele Klassen verteilt ist. Das fragile Basisklassenproblem bedeutet, dass Sicherheitsfixes in Elternklassen Kindklassenverhalten brechen können. Das Verständnis des vollständigen Verhaltens einer tief vererbten Klasse erfordert die Untersuchung aller Vorfahrenklassen, was es leicht macht, Sicherheitsschwachstellen zu übersehen. Methodenüberschreibung über viele Ebenen kann zu unerwartetem Verhalten führen. In Elternklassen implementierte Sicherheitskontrollen können versehentlich von Kindklassen umgangen werden.
Lösung
Bevorzugen Sie Komposition über Vererbung - verwenden Sie enthaltene Objekte anstatt Basisklassen zu erweitern. Wenden Sie das Prinzip flacher Hierarchien an (CISQ empfiehlt max. 7 Ebenen). Verwenden Sie wo möglich Interfaces anstelle abstrakter Basisklassen. Erwägen Sie das Decorator-Pattern zum Hinzufügen von Funktionalität. Refaktorisieren Sie tiefe Hierarchien durch Extrahieren gemeinsamer Funktionalität in Komposition. Verwenden Sie Mixins oder Traits in Sprachen, die sie unterstützen. Wenden Sie das Single-Responsibility-Prinzip auf jede Klasse an. Überprüfen Sie Vererbungshierarchien während Code-Reviews mit Aufmerksamkeit auf Tiefe.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Andere | Bereich: Ändere Reduzierte Wartbarkeit - Tiefe Hierarchien sind schwer zu verstehen und sicher zu modifizieren. |
| Andere | Bereich: Ändere Erhöhte analytische Komplexität - Sicherheitsanalyse muss durch viele Vorfahrenklassen nachverfolgen. |
| Andere | Bereich: Ändere Qualitätsverschlechterung - Das fragile Basisklassenproblem macht Änderungen riskant. |
Beispielcode
Anfälliger Code
// Anfällig: Übermäßig tiefe Vererbung (8 Ebenen)
public class Entity {
protected Long id;
}
public class NamedEntity extends Entity {
protected String name;
}
public class TimestampedEntity extends NamedEntity {
protected LocalDateTime createdAt;
protected LocalDateTime updatedAt;
}
public class AuditableEntity extends TimestampedEntity {
protected String createdBy;
protected String modifiedBy;
}
public class VersionedEntity extends AuditableEntity {
protected Long version;
}
public class ApprovedEntity extends VersionedEntity {
protected boolean approved;
protected String approvedBy;
}
public class PublishableEntity extends ApprovedEntity {
protected boolean published;
protected LocalDateTime publishedAt;
}
// Ebene 8 - Überschreitet CISQ-Schwellenwert von 7
public class BlogPost extends PublishableEntity {
private String title;
private String content;
private List<String> tags;
// Um BlogPost zu verstehen, muss man verstehen:
// Entity -> NamedEntity -> TimestampedEntity -> AuditableEntity
// -> VersionedEntity -> ApprovedEntity -> PublishableEntity -> BlogPost
//
// Änderungen an irgendeinem Elternteil können diese Klasse brechen!
}
# Anfällig: Tiefe Vererbungskette in Python
class Base:
pass
class Level1(Base):
def process(self):
return "Level1"
class Level2(Level1):
def process(self):
return super().process() + " -> Level2"
class Level3(Level2):
def process(self):
return super().process() + " -> Level3"
class Level4(Level3):
def process(self):
return super().process() + " -> Level4"
class Level5(Level4):
def process(self):
return super().process() + " -> Level5"
class Level6(Level5):
def process(self):
return super().process() + " -> Level6"
class Level7(Level6):
def process(self):
return super().process() + " -> Level7"
class Level8(Level7): # Überschreitet Schwellenwert
def process(self):
return super().process() + " -> Level8"
# Um Level8.process() zu verstehen, muss durch 8 Klassen nachverfolgt werden!
# Method Resolution Order (MRO) wird sehr komplex
Korrigierter Code
// Korrigiert: Komposition über Vererbung
// Anstelle tiefer Hierarchie, Komposition und Interfaces verwenden
public interface Identifiable {
Long getId();
}
public interface Named {
String getName();
}
public interface Timestamped {
LocalDateTime getCreatedAt();
LocalDateTime getUpdatedAt();
}
public interface Auditable {
String getCreatedBy();
String getModifiedBy();
}
public interface Versioned {
Long getVersion();
}
public interface Approvable {
boolean isApproved();
String getApprovedBy();
}
public interface Publishable {
boolean isPublished();
LocalDateTime getPublishedAt();
}
// Korrigiert: Ein-Ebenen-Vererbung mit Komposition
public class BlogPost extends Entity implements
Named, Timestamped, Auditable, Versioned, Approvable, Publishable {
private String name;
private String title;
private String content;
private List<String> tags;
// Komposition: an Hilfsobjekte delegieren
private final TimestampInfo timestamps = new TimestampInfo();
private final AuditInfo audit = new AuditInfo();
private final VersionInfo version = new VersionInfo();
private final ApprovalInfo approval = new ApprovalInfo();
private final PublishInfo publish = new PublishInfo();
@Override
public String getName() { return name; }
@Override
public LocalDateTime getCreatedAt() { return timestamps.getCreatedAt(); }
@Override
public LocalDateTime getUpdatedAt() { return timestamps.getUpdatedAt(); }
@Override
public String getCreatedBy() { return audit.getCreatedBy(); }
@Override
public String getModifiedBy() { return audit.getModifiedBy(); }
@Override
public Long getVersion() { return version.getVersion(); }
@Override
public boolean isApproved() { return approval.isApproved(); }
@Override
public String getApprovedBy() { return approval.getApprovedBy(); }
@Override
public boolean isPublished() { return publish.isPublished(); }
@Override
public LocalDateTime getPublishedAt() { return publish.getPublishedAt(); }
}
// Hilfsklassen sind einfach, fokussiert und wiederverwendbar
@Embeddable
public class TimestampInfo {
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// Getter, Setter
}
@Embeddable
public class AuditInfo {
private String createdBy;
private String modifiedBy;
// Getter, Setter
}
# Korrigiert: Mixins und Komposition statt tiefer Vererbung
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional, List
# Mixins für geteilte Funktionalität (flach, fokussiert)
class ProcessingMixin:
def process(self):
raise NotImplementedError
class LoggingMixin:
def log(self, message):
print(f"[{datetime.now()}] {message}")
class ValidationMixin:
def validate(self):
pass # In konkreter Klasse überschreiben
# Kompositions-Helfer
@dataclass
class TimestampData:
created_at: datetime = field(default_factory=datetime.now)
updated_at: Optional[datetime] = None
@dataclass
class AuditData:
created_by: str = ""
modified_by: str = ""
@dataclass
class VersionData:
version: int = 1
# Korrigiert: Flache Klasse mit Komposition und Mixins
@dataclass
class BlogPost(ProcessingMixin, LoggingMixin, ValidationMixin):
"""Blog-Beitrag mit Komposition statt tiefer Vererbung."""
id: int
title: str
content: str
tags: List[str] = field(default_factory=list)
# Komposition statt Vererbung
timestamps: TimestampData = field(default_factory=TimestampData)
audit: AuditData = field(default_factory=AuditData)
version: VersionData = field(default_factory=VersionData)
published: bool = False
approved: bool = False
def process(self):
self.log(f"Verarbeite Beitrag: {self.title}")
self.validate()
return f"Verarbeitet: {self.title}"
def validate(self):
if not self.title:
raise ValueError("Titel erforderlich")
if not self.content:
raise ValueError("Inhalt erforderlich")
def publish(self, publisher: str):
if not self.approved:
raise PermissionError("Beitrag muss vor Veröffentlichung genehmigt werden")
self.published = True
self.timestamps.updated_at = datetime.now()
self.audit.modified_by = publisher
# Maximale Vererbungstiefe: 1 (object -> BlogPost)
# Funktionalität hinzugefügt via Mixins und Komposition
// Korrigiert: Decorator-Pattern und Komposition
// Anstelle tiefer Controller-Hierarchie Filter und Middleware verwenden
public interface IController
{
Task<IActionResult> Execute(ControllerContext context);
}
// Einfacher Basis-Controller
public abstract class BaseController : IController
{
public abstract Task<IActionResult> Execute(ControllerContext context);
}
// Korrigiert: Attribute/Filter statt Vererbung verwenden
[Authenticate]
[Authorize("OrderManagement")]
[Log]
[Cache(Duration = 60)]
[Transactional]
[Audit]
public class OrderController : BaseController
{
private readonly IOrderService _orderService;
private readonly ILogger<OrderController> _logger;
public OrderController(IOrderService orderService, ILogger<OrderController> logger)
{
_orderService = orderService;
_logger = logger;
}
public override async Task<IActionResult> Execute(ControllerContext context)
{
// Fokussierte Controller-Logik - Querschnittsbelange durch Filter behandelt
var order = await _orderService.GetOrderAsync(context.OrderId);
return new OkResult(order);
}
}
// Querschnittsbelange als separate Filter-Klassen
public class AuthenticateAttribute : ActionFilterAttribute
{
public override async Task OnActionExecutionAsync(
ActionExecutingContext context, ActionExecutionDelegate next)
{
if (!context.HttpContext.User.Identity.IsAuthenticated)
{
context.Result = new UnauthorizedResult();
return;
}
await next();
}
}
// Alternative: Decorator-Pattern verwenden
public class LoggingControllerDecorator : IController
{
private readonly IController _inner;
private readonly ILogger _logger;
public LoggingControllerDecorator(IController inner, ILogger logger)
{
_inner = inner;
_logger = logger;
}
public async Task<IActionResult> Execute(ControllerContext context)
{
_logger.LogInformation("Vor Ausführung");
var result = await _inner.Execute(context);
_logger.LogInformation("Nach Ausführung");
return result;
}
}
CVE-Beispiele
Diese CWE ist für direkte CVE-Zuordnung als VERBOTEN markiert, da sie ein Codequalitäts-/Wartbarkeitsproblem und keine direkte Sicherheitsschwachstelle darstellt.
Verwandte CWEs
- CWE-1093: Excessively Complex Data Representation (Eltern)
- CWE-1055: Multiple Inheritance from Concrete Classes (verwandt)
- CWE-1226: Complexity Issues (Kategoriemitglied)
Referenzen
-
MITRE Corporation. "CWE-1074: Class with Excessively Deep Inheritance." https://cwe.mitre.org/data/definitions/1074.html
-
CISQ. "Automated Source Code Quality Measures."
-
Gamma, Erich et al. "Design Patterns" - Bevorzuge Komposition über Vererbung.