Freitag, 21. Februar 2014

Die Attributierung der Objekte in der Umsetzung.

Ein System zur Anforderungsentwicklung in der Umsetzung.

Artikelübersicht
1. Teil Mit dem Zustandsmuster beginnt die Umsetzung eines Requirements Engineering.
2. Teil Die Attributierung der Objekte in der Umsetzung.


Im zweiten Umsetzungspost zum Requirements Engineering möchte ich auf spezifische Ableitungen des RequirementsObject zu sprechen kommen. Bisher sind uns einige spezifische Objekte des Objekt-Aufgaben-Modells in den theoretischen Posts über den Weg gelaufen. Zuerst ist uns das Ideenobjekt (Idea) begegnet. Die unterschiedlichen Stakeholder haben die Möglichkeit Ideen zu sammeln. Später werden diese in Themen (Issue) zusammengefasst. Für Themen werden Systemkontextuntersuchungen veranlasst, in denen Kontextobjekte (Context) geschaffen werden. Um planbare Systeme zu schaffen, habe ich an dieser Stelle User Stories (UserStory) eingeführt. Natürlich wird es im weiteren Verlauf des Requirements Engineering weitere Objekte geben.

Das Ideenobjekt fällt in der Reihe der Objekte ein bisschen aus dem Rahmen. Es ist das einzige Objekt, für das keine Aufgabe veranlasst werden kann. Jeder berechtigte Stakeholder kann seinen Ideen freien Raum lassen. Vielleicht wird es im späteren Verlauf weitere Objekte geben, die von dieser Art sind. Aus diesem Grund werde ich ein Interface Realizable einführen. Alle Objekte, die von diesem Interface abgeleitet wurden, werden durch Aufgaben (RequirementsTask) realisiert. Dieses Interface stellt also sicher, dass ein RequirementsTask hinzugefügt wird. Bestimmte Zustandswechsel eines Objekts sind abhängig von den Zustandswechseln der Aufgabe. ( Bedingte Zustandswechsel auf die ich in einem späteren Post eingehen werde. )

Im Folgenden werde ich die Attribute der Objekte besprechen. Dabei werden alle gemeinsamen Attribute in der abstrakten Klasse RequirementsObject abgebildet. [POHL08] hat in seinem Buch über das Requirements Engineering Kategorien für Attribute von Anforderungen aufgeführt. Beim Durchdenken der Erfordernisse werde ich diese Kategorien als Wegweiser benutzen. Die erste Kategorie ist die Identifizierbarkeit. Jedes Objekt wird eine eindeutige id besitzen. Diese id ist im RequirementsObject angesiedelt. In den spezifischen Objekten gibt es einen zusätzlichen konstanten Bezeichner, mit dem die Art des Objektes analysiert werden kann. Vom Benutzer kann dem spezifischen Objekt ein frei vergebbarer Name hinzugefügt werden.

Die zweite Kategorie von [POHL08] sind die Kontextbeziehungen. Kontextbeziehungen umfassen "Attribute, die zur Dokumentation von unmittelbaren Kontextbeziehungen des Anforderungsartefakts verwendet werden." [S.260, POH08] Ein Objekt ist aus dem Kontext hervorgegangen. Bestimmte Stakeholder erstellen das Objekt. Dabei verwenden sie verschiedene Quellen, aus denen das neue Objekt entsteht. Am Ende haben bestimmte Stakeholder einen Nutzen von diesem Anforderungsartefakt. Alle diese Objekte stammen aus dem Kontext und sind demzufolge Ableitungen aus dem Objekt Context. Dabei habe ich erst einmal die Objekte Stakeholder und Document abgeleitet. Die Klasse Context wird somit zu einer abstrakten Klasse, weil sie eher eine Abstraktion der spezifischen Kontextobjekte ist. Im Post Welche Arten von Kontextobjekten gibt es? werden weitere spezifische Kontextobjekte aufgeführt.

Die dritte Kategorie sind Dokumentationsaspekte. Hier erscheinen mir besonders die Statusinformationen als wichtig. [POHL08] empfiehlt folgende Werte: ungeprüft, in Prüfung, partiell geprüft, geprüft, in Korrektur und freigegeben. Innerhalb meines Objekt-Aufgaben-Modells wird dieser Status jedoch durch den Zustand von Objekt und Aufgabe abgebildet. Ein besonders Attribut zum setzen des Objektstatus ist deswegen nicht notwendig.

Die vierte Kategorie sind Inhaltsaspekte. Dieses Kategorie umfasst z.B. eine Klassifizierung der Anforderungsartefakte. Diese Klassifizierung wird in meiner Umsetzung durch die Ableitungshierarchie der abstrakten Klasse RequirementsObject verwirklicht. Jedes Anforderungsartefakt sollte über eine prägnante Kurzbeschreibung (shortDescription) verfügen. Eine längerer Text kann in den ergänzenden Informationen abgelegt werden. Ich möchte dieses Attribut ausführliche Beschreibung (detailedDescription) nennen.

Die fünfte Kategorie "umfasst mögliche Attribute zur Dokumentation von Informationen über die Übereinstimmung der Stakeholder bzgl. einer Anforderung." [S.263, POHL08] Unter Übereinstimmung versteht [POHL08], dass alle Stakeholder sich ein gemeinsames Verständnis zu den Anforderungsartefakten erarbeitet haben und somit alle Konflikte gelöst sind. Um Konflikte zu lösen, müssen sie erst einmal erkannt werden. Dazu können Stakeholder erkannte Konflikte kennzeichnen. Diese Konflikte müssen mit geeigneten Methoden gelöst werden. Mit einer Lösung sind Entscheidungen verbunden, die ebenfalls vermerkt werden sollten. Für diese Tätigkeit habe ich eine Klasse Conflict entworfen. In dieser Klasse wird der Konflikt durch einen Stakeholder beschrieben. Weitere Stakeholder können Bemerkungen zum Konflikt machen und es wird außerdem eine Entscheidung mit einem verantwortlichen Stakeholder angelegt. Alle Konflikte sind bei der Neuanlage im Status ungelöst und werden nach einer geeigneten Lösung auf gelöst gesetzt.

"Die sechste Kategorie umfasst mögliche Anforderungsattribute zur Dokumentation der Validierung einer Anforderung." [S.263, POHL08] Die Attribute für diese Validierungsaspekte hoffe ich durch das Zustandsmodell für Objekte und Aufgaben gelöst zu haben. Die letzte Kategorie sind die Managementaspekte. Hier fallen Attribute wie Risiko, Kritikalität, Priorität, Stabilität, Version und Aufwand hinein. Diese Aspekte möchte ich jedoch erst einmal im theoretischem Teil beschreiben, bevor ich hier eine praktische Umsetzung vollziehe.

In diesem Artikel habe ich allgemeine Attribute für das RequirementsObject angedacht. Dabei bin ich den Kategorien von [POHL08] gefolgt. Der dargestellt erste Umsetzungsvorschlag ist offen für Refactoringmaßnahmen. Dazu würde ich auch die Diskussion notwendiger oder überflüssiger Attribute, Klassen und Interfaces zählen.



Im Folgenden zeige ich den Code dieser ersten Version. Auf die Zeilen, die bereits im Post zum Zustandsmodell dargestellt wurden, verzichte ich hier.

// Diese Klasse ist jetzt eine abstrakte Klasse
public abstract class RequirementsObject {
 // Kategorie 1
 // ID
 private Integer id;
 // benutzervergebener Name
 private String name;
 
 // Kategorie 2
 // Ersteller des Objektes 
 private List<Stakeholder> creators = new ArrayList<Stakeholder>();
 // Nutzer des Ergebnisses des Objektes;
 private List<Stakeholder> winners = new ArrayList<Stakeholder>();
 // Quellen
 private List<Context> sources = new ArrayList<Context>();
 
 // Kategorie 4
 // Kurzbeschreibung
 private String shortDescription;
 // ausführliche Beschreibung
 private String detailedDescription;
 
 // Kategorie 5
 // Konflikte 
 private List<Conflict> conflicts = new ArrayList<Conflict>();
 
 public RequirementsObject(String name, Stakeholder creator, String shortDescription) {
  this.name = name;
  creators.add(creator);
  this.shortDescription = shortDescription;
  create();
 }

 // Zustand
 RequirementsObjectState requirementsObjectState;

  private void create() {
      requirementsObjectState = RequirementsObjectState.create();
  }

  public void prioritize() {
      requirementsObjectState = requirementsObjectState.prioritize();
  }

  public void defer() {
      requirementsObjectState = requirementsObjectState.defer();
  }

  public void delete() {
      requirementsObjectState = requirementsObjectState.delete();
  }

  public void represent() {
      requirementsObjectState = requirementsObjectState.represent();
  }

  public void order() {
      requirementsObjectState = requirementsObjectState.order();
  }

  public void implement() {
      requirementsObjectState = requirementsObjectState.implement();
  }

  public RequirementsObjectState getRequirementsObjectState() {
      return requirementsObjectState;
  }

 public Integer getId() {
  return id;
 }

 public String getIdentifier() {
  throw new IllegalArgumentException("Identifier is not implemented!");
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public List<Stakeholder> getCreators() {
  return creators;
 }

 public void addCreator(Stakeholder creator) {
  creators.add(creator);
 }

 public List<Stakeholder> getWinners() {
  return winners;
 }

 public void addWinner(Stakeholder winner) {
  winners.add(winner);
 }

 public List<Context> getSources() {
  return sources;
 }

 public void addSource(Context source) {
  sources.add(source);
 }

 public String getShortDescription() {
  return shortDescription;
 }

 public void setShortDescription(String shortDescription) {
  this.shortDescription = shortDescription;
 }

 public String getDetailedDescription() {
  return detailedDescription;
 }

 public void setDetailedDescription(String detailedDescription) {
  this.detailedDescription = detailedDescription;
 }

 public List<Conflict> getConflicts() {
  return conflicts;
 }

 public void addConflict(Conflict conflict) {
  conflicts.add(conflict);
 }

}

public class Idea extends RequirementsObject {
 private static final String IDENTIFIER = "IDEA";

 public Idea(String name, Stakeholder creator, String shortDescription) {
  super(name, creator, shortDescription);
 }

 public String getIdentifier() {
  return IDENTIFIER;
 }
}

public interface Realizable {
 RequirementsTask getRequirementsTask();
 void setRequirementsTask(RequirementsTask requirementsTask);
}

public class Issue extends RequirementsObject implements Realizable {
 private static final String IDENTIFIER = "ISSUE";
 private RequirementsTask requirementsTask;

 public Issue(String name, Stakeholder creator, String shortDescription) {
  super(name, creator, shortDescription);
 }

 public String getIdentifier() {
  return IDENTIFIER;
 }

 @Override
 public RequirementsTask getRequirementsTask() {
  return requirementsTask;
 }

 @Override
 public void setRequirementsTask(RequirementsTask requirementsTask) {
  this.requirementsTask = requirementsTask;
 }
}

// Diese Klasse ist abstrakt, weil davon nur spezifische Objekte erzeugt werden können 
public abstract class Context extends RequirementsObject implements Realizable {
 private static final String IDENTIFIER = "CONTEXT";
 private RequirementsTask requirementsTask;

 public Context(String name, Stakeholder creator, String shortDescription) {
  super(name, creator, shortDescription);
 }

 public String getIdentifier() {
  return IDENTIFIER;
 }

 @Override
 public RequirementsTask getRequirementsTask() {
  return requirementsTask;
 }

 @Override
 public void setRequirementsTask(RequirementsTask requirementsTask) {
  this.requirementsTask = requirementsTask;
 }
}

public class UserStory extends RequirementsObject implements Realizable {
 private static final String IDENTIFIER = "USERSTORY";
 private RequirementsTask requirementsTask;

 public UserStory(String name, Stakeholder creator, String shortDescription) {
  super(name, creator, shortDescription);
 }

 public String getIdentifier() {
  return IDENTIFIER;
 }

 @Override
 public RequirementsTask getRequirementsTask() {
  return requirementsTask;
 }

 @Override
 public void setRequirementsTask(RequirementsTask requirementsTask) {
  this.requirementsTask = requirementsTask;
 }
}

public class Stakeholder extends Context {
 private String firstname;
 private String lastname;
 private static Stakeholder systemStakeholder = new Stakeholder("System", null, "Der Systemuser", null, null);
 
 public Stakeholder(String name, Stakeholder creator, String shortDescription, String firstname, String lastname) {
  super(lastname, creator, shortDescription);
  this.firstname = firstname;
  this.lastname = lastname;
 }

 public static Stakeholder getSystemStakeholder() {
  return systemStakeholder;
 }

 public String getFirstname() {
  return firstname;
 }

 public void setFirstname(String firstname) {
  this.firstname = firstname;
 }

 public String getLastname() {
  return lastname;
 }

 public void setLastname(String lastname) {
  this.lastname = lastname;
 }
}

public class Document extends Context {
 private static Stakeholder systemStakeholder = new Stakeholder("System", null, "Der Systemuser", null, null);
 
 public Document(String name, Stakeholder creator, String shortDescription) {
  super(name, creator, shortDescription);
 }

 public static Stakeholder getSystemStakeholder() {
  return systemStakeholder;
 }
 
}

public class Conflict {
 // Erzeuger
 private Stakeholder creator;
 // Konfliktbeschreibung
 private String description;
 // Lösungsstatus
 private boolean solved;
 // Bemerkungen zum Konflikt
 private Map<Stakeholder,String> notices = new HashMap<Stakeholder, String>();
 // Entscheidungen zum Konflikt
 private Map<Stakeholder,String> decisions = new HashMap<Stakeholder, String>();
 
 public String getDescription() {
  return description;
 }
 
 public void setDescription(String description) {
  this.description = description;
 }
 
 public boolean isSolved() {
  return solved;
 }
 
 public void setSolved(boolean solved) {
  this.solved = solved;
 }
 
 public Map<Stakeholder, String> getNotices() {
  return notices;
 }
 
 public void putNotice(Stakeholder attendee, String notice) {
  notices.put(attendee, notice);
 }
 
 public Map<Stakeholder, String> getDecisions() {
  return decisions;
 }
 
 public void putDecisions(Stakeholder attendee, String decision) {
  decisions.put(attendee, decision);
 } 
}

public class RequirementsTask {
    RequirementsTaskState requirementsTaskState;

    public void create() {
        requirementsTaskState = RequirementsTaskState.create();
    }

    public void implement() {
        requirementsTaskState = requirementsTaskState.implement();
    }

    public void positiveReview() {
        requirementsTaskState = requirementsTaskState.positiveReview();
    }

    public void negativeReview() {
        requirementsTaskState = requirementsTaskState.negativeReview();
    }

    public void acceptance() {
        requirementsTaskState = requirementsTaskState.acceptance();
    }

    public void negativeAcceptance() {
        requirementsTaskState = requirementsTaskState.negativeAcceptance();
    }

    public void abort() {
        requirementsTaskState = requirementsTaskState.abort();
    }

    public RequirementsTaskState getRequirementsTaskState() {
        return requirementsTaskState;
    }
}



  • [POHL08] Klaus Pohl: „Requirements Engineering“, 2. korrigierte Auflage, dpunkt.verlag GmbH, Heidelberg, 2008


vorheriger Post dieses Themas


Print Friendly Version of this page Print Get a PDF version of this webpage PDF

Keine Kommentare:

Kommentar veröffentlichen