CLASS DIAGRAM SOFTWARE ENGERING

182 views
145 views

Published on

Published in: Education, Technology, Business
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
182
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
5
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

CLASS DIAGRAM SOFTWARE ENGERING

  1. 1. Software Engineering I (02161) Assoc. Prof. Hubert Baumeister Spring 2012 Contents 1 Refactoring 1 2 Class Diagrams 11 2.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.2 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.3 Unidirectional Associations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.4 Implementing Associations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.5 Bi-directional associations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.6 Generalisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.7 Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.8 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 1 Refactoring Refactoring • Refactoring is to restructure the software without changing its functionality • Goal: Improve the design of the software • With agile software development this contributes to the design activity • This is step is necessary to keep the software simple and adaptable • Sufficient (automated) tests are prerequisite to refactoring – Without tests it is not ensured the refactoring does not destroy existing functionality → regression tests Refactoring: Improving the Design of Existing Code (1999) • Fowler describes – a set of refactorings, – when to apply them, (based on code smells) – and how they are done (Mechanics) ∗ proceed in small steps → each step should lead from a compilable program that passes the tests to a compilable program that passes the tests • E.g. renameMethod, extractMethod, encapsulateField, encapsulateCollection, ... → For a complete list see http://www.refactoring.com/catalog/index.html 1
  2. 2. Example refactoring: RenameMethod • Motivation – Sometimes a method name does not express precisely what the method is doing – This can hinder the understanding of the code; thus give the method a more intention revealing name • Mechanics 1) Create a method with the new name 2) Copy the old body into the new method 3) In the old body replace the body by a call to the new method; compile and test 4) Find all the references to the old method and replace it with the new name; compile and test 5) Remove the old method; compile and test → Supported directly in some IDE’s • Note that Eclipse has nice support for this refactoring → Together with the support from Eclipse, this becomes a very powerful tool to improve ones code Code smells If it stinks, change it Refactoring, Martin Fowler, 1999 • Duplicate Code • Long Method • Large Class • Long Parameter List • Divergent Change • Shotgun Surgery • Feature Envy • Data Clumps • Primitive Obsession • Switch Statements • Parallel Inheritance • Lazy Class • Speculative Generalisation • Temporary Field • Message Chains • MiddleMan • Inappropriate Intimacy • Alternative Classes With Different Interfaces • Incomplete Library • Data Class • Refused Bequest • Comments 2
  3. 3. Code Smell: Data Clumps public class Person { private String name; private Calendar birthdate; private Company company; private String street; private String city; private String zip; ... } public class Company { private String name; private String vat_number; private String street; private String city; private String zip; ... } Street, city, and zip are data clumps, i.e., they belong together and appear in different places as attributes of objects. They should made into their own class (Address) and Company and Person should have each a field of type Address. Code Smell: Switch Statement public class User { public double computeFine() { double fine = 0; for (Medium m : borrowedMedia) { if (m.overdue) { switch (m.getType()) { case Medium.BOOK : fine = fine + 10; break; case Medium.DVD: fine = fine + 30; break; case Medium.CD: fine = fine + 20; break; default fine = fine + 5; break; } } } return fine; } } Better design public class User { public double computeFine() { double fine = 0; for (Medium m : borrowedMedia) { if (m.overdue) { fine = fine + m.getFine();} } return fine; } } public class Medium { public double getFine() { return 5; } } public class Book extends Medium { public double getFine() { return 10; } } public class DVD extends Medium { public double getFine() { return 30; } } public class CD extends Medium { public double getFine() { return 20; } } MarriageAgency code Method matchCustomer in class MarriageAgency 3
  4. 4. package dtu.se.marriageagency; import java.util.ArrayList; public class MarriageAgency { private List<Customer> customers; public void setCustomers(List<Customer> customers2) { customers = new ArrayList<Customer>(customers2); } public List<Customer> matchCustomer(Customer customer) { List<Customer> res = new ArrayList<Customer>(); for (Customer potential : customers) { // A matching partner needs to have the opposite sex if (potential.getSex() != customer.getSex()) { // The age difference between matching partners // should be less than 10 int yearDiff = Math.abs(potential.getBirthYear() - customer.getBirthYear()); if (yearDiff <= 10) { for (String interest : potential.getInterests()) { // The customer and the potential candidate should // have one interest in common if (customer.getInterests().contains(interest)) { res.add(potential); break; } } } } } return res; } } Tests for MarriageAgency Refactoring is best applied with a strong test suite. Here is the test suite for the MarriageAgency: package dtu.se.marriageagency; import static org.junit.Assert.*; public class TestMarriageAgency { MarriageAgency agency; Customer customer1, customer2; @Before public void setUp() { agency = new MarriageAgency(); List<Customer> customers = new ArrayList<Customer>(); agency.setCustomers(customers); customer1 = new Customer("man",1970); customer1.addInterest("skiing"); customer1.addInterest("check"); customers.add(customer1); customer2 = new Customer("man",1950); customer2.addInterest("biking"); customer2.addInterest("photograpy"); customers.add(customer2); agency.setCustomers(customers); } @Test public void testMatch() { Customer customer = new Customer("woman",1975); customer.addInterest("skiing"); customer.addInterest("biking"); List<Customer> matches = agency.matchCustomer(customer); assertEquals(1,matches.size()); assertSame(customer1,matches.get(0)); } 4
  5. 5. @Test public void testMatchNoCommonInterests() { Customer customer = new Customer("woman",1975); customer.addInterest("reading"); customer.addInterest("cycling"); List<Customer> matches = agency.matchCustomer(customer); assertEquals(0,matches.size()); } @Test public void testMatchAgeDifferenceTooBig() { Customer customer = new Customer("woman",1981); customer.addInterest("skiing"); customer.addInterest("biking"); List<Customer> matches = agency.matchCustomer(customer); assertEquals(0,matches.size()); } @Test public void testMatchWrongSex() { Customer customer = new Customer("man",1975); customer.addInterest("skiing"); customer.addInterest("biking"); List<Customer> matches = agency.matchCustomer(customer); assertEquals(0,matches.size()); } } Bad Smell: Long methods • Methods in object-oriented programs should be short • They should do a lot of delegation • Long methods are an indication that an object is doing too much work by themselves → We already discussed this when we discussed about centralised- and decentralised control • Solution – Use ExtractMethod to introduce delegation Bad Smell: Comments • Comments are good if they explain things that are not visible in the code, e.g. design decisions (why you wrote the code in this way) • Sometimes comments try to hide badly written code; they act as a deodorant to cover the bad smells • Solution – Remove the underlying bad design and see if the comments are still necessary in the refactored code – Use ExtractMethod or RenameMethod to let the code speak for itself Refactoring: ExtractMethod • Motivation: – For code fragments that can be grouped together introduce a new method • Mechanics 1) Create a new method with a name revealing the intention of the code 2) Copy the code fragment into the new method → Complications: references to variables defined outside the scope of the code fragment have to passed as parameter 5
  6. 6. → Sometimes other refactorings have to be done before ExtractMethod becomes possible (e.g. in our example to InlineTemp • Note that Eclipse has nice support for this refactoring → Together with the support from Eclipse, this becomes a very powerful tool to improve ones code Eclipse: ExtractMethod: hasOppositeSex private boolean hasOppositeSex(Customer customer, Customer potential) { return potential.getSex() != customer.getSex(); } Note that we don’t need the comment about the opposite sex anymore. The name of the method hasOppo- siteSex takes over this role. public List<Customer> matchCustomer(Customer customer) { List<Customer> res = new ArrayList<Customer>(); for (Customer potential : customers) { if (hasOppositeSex(customer, potential)) { // The age difference between matching partners // should be less than 10 int yearDiff = Math.abs(potential.getBirthYear() - customer.getBirthYear()); if (yearDiff <= 10) { for (String interest : potential.getInterests()) { // The customer and the potential candidate should // have one interest in common if (customer.getInterests().contains(interest)) { res.add(potential); break; } } } } } return res; } Eclipse: ExtractMethod: hasAppropriateAge To apply extract method to introduce a new method hasAppropriateAge, it is a good idea to inline the compu- tation of yearDiff, so that the arguments to hasAppropriateAge are both of class customer.: 1. Step: inline temp yearDiff public List<Customer> matchCustomer(Customer customer) { List<Customer> res = new ArrayList<Customer>(); for (Customer potential : customers) { if (hasOppositeSex(customer, potential)) { // The age difference between matching partners // should be less than 10 if (Math.abs(potential.getBirthYear() - customer.getBirthYear()) <= 10) { for (String interest : potential.getInterests()) { // The customer and the potential candidate should // have one interest in common if (customer.getInterests().contains(interest)) { res.add(potential); break; } } } } } return res; } 2. Step: extract method hasAppropriate Age: 6
  7. 7. private boolean hasAppropriateAge(Customer customer, Customer potential) { return Math.abs(potential.getBirthYear() - customer.getBirthYear()) <= 10; } public List<Customer> matchCustomer(Customer customer) { List<Customer> res = new ArrayList<Customer>(); for (Customer potential : customers) { if (hasOppositeSex(customer, potential)) { if (hasAppropriateAge(customer, potential)) { for (String interest : potential.getInterests()) { // The customer and the potential candidate should // have one interest in common if (customer.getInterests().contains(interest)) { res.add(potential); break; } } } } } return res; } Refactoring: InlineTemp • Motivation: – A temp is assigned to once and comes in the way of further refactorings • Mechanics 1) Declare the temp as final and compile; Why is this step helpful? 2) Find all references of temp and replace them with the right hand side of the expression; compile and test 3) Remove the declaration of temp and the assignment to temp; compile and test • Note: There exists also an opposite refactoring that introduces temps for expressions → A lot of refactorings have versions that do exactly the opposite → Refactorings should be used to avoid code smells Extract method: hasOneInterestInCommon We also extract the computation of a common interest in one method called hasOneInterestInCommon private void hasOneInterestInCommon(Customer customer, List<Customer> res, Customer potential) { for (String interest : potential.getInterests()) { if (customer.getInterests().contains(interest)) { res.add(potential); break; } } } public List<Customer> matchCustomer(Customer customer) { List<Customer> res = new ArrayList<Customer>(); for (Customer potential : customers) { // A matching partner needs to have the opposite sex if (hasOppositeSex(customer, potential)) { // The age difference between matching partners // should be less than 10 if (hasAppropriateAge(customer, potential)) { hasOneInterestInCommon(customer, res, potential); } } } return res; } 7
  8. 8. Code smell: A method is doing two things • A method should usually do one thing and only one thing and that good. • If a method tries do to too many things, try to split the method in several methods • In our example hasOneInterestInCommon determines what it means to have one interest in common and also adds a potential customer to the list of matches ExtractMethod match We can still do a further refactoring by separating the concern of going through the list of potential candidates and computing the match itself. private void match(Customer customer, List<Customer> res, Customer potential) { if (hasOppositeSex(customer, potential)) { if (hasAppropriateAge(customer, potential)) { if (hasOneInterestInCommon(customer, potential)) { res.add(potential); } } } } public List<Customer> matchCustomer(Customer customer) { List<Customer> res = new ArrayList<Customer>(); for (Customer potential : customers) { match(customer, res, potential); } return res; } The match method has now a similar problem as the void method hasOneInterestInCommon. It takes the result list as an argument and modifies the result list. The same trick as with hasOneInterestInCommon can be used to actually get. private boolean match(Customer customer, Customer potential) { boolean match = false; if (hasOppositeSex(customer, potential)) { if (hasAppropriateAge(customer, potential)) { if (hasOneInterestInCommon(customer, potential)) { match = true; } } } return match; } public List<Customer> matchCustomer(Customer customer) { List<Customer> res = new ArrayList<Customer>(); for (Customer potential : customers) { if(match(customer, potential)) { res.add(potential); } } return res; } Code smell: Inappropriate Intimacy • If a class uses too many features of another class this can be a sign that code and fields may need to move to other classes → In our example, class MariageAgency itself computes the matching conditions instead of the delegating this to the customer class 8
  9. 9. Refactoring: Move Method • Motivation – A method is using or used by more features of another class than the class on which it is defined – Move that method to the other class – E.g. the methods hasOppositeSex, hasAppropriateAge, hasOneInterestInCommon use instance vari- ables of the customer object; thus these should be methods of class customer • Mechanics 1) Declare the method in the target class 2) Copy the body of the old method to the new method 3) If necessary, pass on the original object 4) Replace the body of the old method by the call to the new method; compile and test 5) Replace all references to the old method with calls to the new method; compile and test 6) Remove the old method; compile and test MoveMethod hasOppositeSex, hasAppropriateAge, and hasOneInterestInCommon We now move the methods hasOppositeSex, hasAppropriateAge, and hasOneInterestInCommon from the class MariageAgency to their first arguments (a customer). This results in the following methods in class customer. Note that the arguments of the methods have changed. The first argument is now this, the customer on which the methods are called. We also do the same with method match, as this looks like a responsibility of the customer and not the MarriageAgency. public class Customer { private String sex; private int birthYear; private List<String> interests = new ArrayList<String>(); ... boolean hasOneInterestInCommon(Customer potential) { boolean hasOneInterestInCommon = false; for (String interest : potential.getInterests()) { if (getInterests().contains(interest)) { hasOneInterestInCommon = true; break; } } return hasOneInterestInCommon; } boolean hasAppropriateAge(Customer potential) { return Math.abs(potential.getBirthYear() - getBirthYear()) <= 10; } boolean hasOppositeSex(Customer potential) { return potential.getSex() != getSex(); } boolean match(Customer potential) { boolean match = false; if (hasOppositeSex(potential)) { if (hasAppropriateAge(potential)) { if (hasOneInterestInCommon(potential)) { match = true; } } } return match; } } We can still do two simplifactions. The first is to convert the if statements that return a boolean value into a boolean expression. This shortens the program code and enhances the clarity of the code. The second change is that we return from the loop directly when we have found an element instead of breaking out of the loop. The following is the final class customer. 9
  10. 10. public class Customer { private String sex; private int birthYear; private List<String> interests = new ArrayList<String>(); public Customer(String sex, int birthYear) { this.sex = sex; this.birthYear = birthYear; } public String getSex() { return sex; } public int getBirthYear() { return birthYear; } public List<String> getInterests() { return interests; } public void addInterest(String interest) { interests.add(interest); } boolean hasOneInterestInCommon(Customer potential) { for (String interest : potential.getInterests()) { if (getInterests().contains(interest)) { return true; } } return false; } boolean hasAppropriateAge(Customer potential) { return Math.abs(potential.getBirthYear() - getBirthYear()) <= 10; } boolean hasOppositeSex(Customer potential) { return potential.getSex() != getSex(); } boolean match(Customer potential) { return hasOppositeSex(potential) & hasAppropriateAge(potential) & hasOneInterestInCommon(potential); } } The following remains in the class MarriageAgency public class MarriageAgency { private List<Customer> customers; public void setCustomers(List<Customer> customers2) { customers = new ArrayList<Customer>(customers2); } public List<Customer> matchCustomer(Customer customer) { List<Customer> res = new ArrayList<Customer>(); for (Customer potential : customers) { if(customer.match(potential)) { res.add(potential); } } return res; } } Advantages of the new design The new design is much easier to read than the original design, because each method is now responsible for one thing only and has a name that shows what it is responsible for. In addition, we have moved the responsibilities 10
  11. 11. into the right class. For example, the code related to the actual matching has been moved to the customer class. The remaining responsibility of the MarriageAgency is going through the list of its customer but does not have anymore the responsibility to determine when one customer matches another. Because we know have small methods that are only responsible.for one task, the algorithm can be simply modified. We could have a special type of customer (i.e. a subclass of customer) that defines hasAppropriateAge in a different way. Also, we could add a gay customer to the system. He would not look for the opposite sex, but for a same sex partner. The implementation will override hasOppositeSex to return true if the sex is the same. However, then the name hasOppositeSex does not fit anymore, and the method should be renamed hasAppropriateSex. Summary • Refactoring improves the quality of the code by removing code smells • Refactoring is applied to prepare the code for adding new functionality and improving the code of existing functionality (after, e.g., new functionality has been added) → Refactoring plays an important role in good design • Refactoring depends on sufficient test cases • Refactoring itself are described through their mechanics – Small steps leading from correct (compilable and tests pass) to correct programs → Today, IDE’s support the most important refactorings 2 Class Diagrams 2.1 Introduction Unified Modelling Language (UML) The Unified Modelling Language is a set of graphical notations for describing object-oriented systems. The UML contains several types of diagrams: e.g. class diagrams, component diagrams, state machines, sequence diagrams etc. In the 90’s saw the definition of several object-oriented methods with corresponding graphical notations. Three designers of these methods joined the company rational to define a unified language based on their contributions. The result was the UML which defined a common notation for object-oriented systems. Soon after, the development of the UML was taken over by the Object Management Group (OMG) and resulted in an ISO standard. Class Diagram I Class diagrams can be used for different purposes 1 to give an overview over the domain concepts – as part of the requirements analysis (e.g. a graphical form representation supplementing the glossary) 2 to give an overview over the system – as part of the design of the system 3 to give an overview over the systems implementation 4 ... 11
  12. 12. Class Diagram Example LibraryMember MemberOfStaff Journal Copy signature isOverdue Book title author publisher edition 0..* 1 copy of 0..1 0..5 borrows 0..1 0..5 borrows Basic concepts of class diagrams • Classes with attributes and operations • Associations with multiplicities and possibly navigability • Generalization of classes (corresponds in principle to subclassing in Java) Why a graphical notation? • What is the structure of this small Java program? public class Assembly extends Component { public double cost() { } public void add(Component c) {} private Collection<Component> components; } public class CatalogueEntry { private String name = ""; public String getName() {} private long number; public long getNumber() {} private double cost; public double getCost() {} } public abstract class Component { public abstract double cost(); } public class Part extends Component { private CatalogueEntry entry; public CatalogueEntry getEntry() {} public double cost(){} public Part(CatalogueEntry entry){} Why a graphical notation? • Same information as in the Java program • However, the structure is visible immediately and better to understand 12
  13. 13. Class Diagrams and Program Code • Class Diagrams were indented as a means to graphically show object-oriented programs • As a consequence: Class Diagrams allow one to model all the structural features of a Java class – e.g. classes, (static) methods, (static) fields, inheritance, ... • However, class diagrams are more abstract than programs – Concepts of associations, aggregation/composition, ... → Modelling with class diagrams is more expressive and abstract than programming in Java → It is important to learn who these abstract, object-oriented concepts embodied in class diagrams are imple- mented in Java programs → Improves your object-oriented design skills Example What is the class diagram for the following program? public class C { private int a; public int getA() { return a; } public void setA(int a) { this.a = a; } } 13
  14. 14. 2.2 Classes Classes • A class describes a collection of objects that have a common characteristics regarding – state (attributes) – behaviour (operations) – relations to other classes (associations and generalisations) • A class ideally should represent only one concept – All the attributes should related to that concept – All the operations should make sense for that concept 14
  15. 15. General correspondence between Classes and Programs KlasseNavn +navn1: String = "abc" -navn2: int #navn3: boolean -f1(a1:int,a2:String []): float +f2(x1:String,x2:boolean): void #f3(a:double): String Klassens navn Attributter Operationer ’-’ : private ’+’ : public ’#’: protected ’navn3’ og ’f1’ er statiske størrelser public class KlasseNavn { private String navn1 = "abc"; private int navn2; protected static boolean navn3; private static float f1(int a1, String[] a2) { ... } public void f2(String x1, boolean x2) { ... } protected String f3(double a) { ... } public String getNavn1(); {...} public void setNavn1(String n) {...} } 2.3 Unidirectional Associations Associatons between classes Example: Persons and their employers: • every person is associated to an employer (company/firma) • every company has 0, 1, or more (’*’) employees (persons) public class Company { .... private Set<Person> employees; .... } public class Person { .... private Company company; .... } Associations between classes Person Firma * 0..1 ansatte 15
  16. 16. • a role (here ansatte) describes objects (here persons) at the end of an association, seen from the objects belonging to the classes at the opposite end of the (here company) • default role name: name of the associated class (e.g. person) • in an implementation a role name is typically a variable. For example: public class Firma { .... private Set<Person> ansatte; .... } Attributes and Associations • There is in principle no distinction between attributes and associations • Associations can be drawn as attributes and vice versa Attributes versus Associations When to use attributes and when to use associations? • Associations – When the target class of an association is shown in the diagram – The target class of an association is a major class of the model ∗ e.g. Part, Assembly, Component, ... • Attributes – When the target class of an associations is not shown in the diagram – With datatypes / Value objects ∗ Datatypes consists of a set of values and set of operations on the values ∗ In contrast to classes are datatypes stateless ∗ e.g. int, boolean, String . . . – Library classes • However, final choice depends on what one wants to express with the diagram – E.g. Is it important to show a relationship to another class? 16
  17. 17. 2.4 Implementing Associations Implementing Associations: Cardinality 1 1 BA • When requesing the field b, the implementation needs to ensure that always a B is returned public class A { private B b = new B(); // 1. possibility public A(B b) { this.b = b;} // 2. possibility public B getB() { // 3. possibility if (b == null) {b = computeB();} return b; } public void setB(B b) { if (b != null) {this.b = b;} } } Implementing Associations: Cardinality * * BA • Uses an attribute of type Collection public class A { private Collection<B> bs = new java.util.ArrayList<B>(); public void addB(B b) { bs.add(b);} public void contains(B b) { return bs.contains(b); } public void removeB(B b) { bs.remove(b); } } • If the multiplicity is >1, one adds a plural s to the role name: b → bs Interface Collection<E> Operation Description boolean add(E e) returns false if e is in the collection boolean remove(E e) returns true if e is in the collection boolean contains(E e) returns true if e is in the collection Iterator<E> iterator() allows to iterate over the collection int size() number of elements [0.5cm] Example of iterating over a collection Collection<String> names = new HashSet<String>() ; names.add("Hans"); ... for (String name : names) { // Do something with name, e.g. System.out.println(name); } 17
  18. 18. Hierarchy of collection interfaces • Collection: Superinterface of all collections • Set: Order is irrelevant; no duplicates allowed • List: Order is relevant; duplicates are allowed; allows positional access in addition to Collection operations – E get(int index); – E set(int index, E element); – void add(int index, E element); – E remove(int index); Collection and their subinterfaces cannot be instantiated directly → One needs to use concrete implementation classes like HashSet or ArrayList Implementing Associations: Cardinality * * BA Default: Unordered, no duplicates public class A { private Set<B> bs = new HashSet<B>(); ... } * {ordered} BA public class A { private List<B> bs = new ArrayList<B>(); ... } Implementing Associations: Cardinality * * BA With UML the default for n-ary associations is: unordered and no duplicates public class A { private Set<B> bs = new HashSet<B>(); ... } 18
  19. 19. If one wants the collection to be ordered with duplicates one has to use {ordered} {ordereA public class A { private List<B> bs = new ArrayList<B>(); ... } Encapsulation problem: getStudents StudentUniversity * University dtu = new University("DTU"); .. Student hans = new Student("Hans"); Set<Student> students = dtu.getStudents(); • Access to the association using getBs (e.g. getStudents()) poses encapsulation problems: Why? • A client of A can change the association without A knowing it! Student hans = new Student("Hans"); students.add(hans); students.remove(ole); ... • Solution: getStudents should return an unmodifiable set public void Collection<Student> getStudents() { students = Collections.unmodifiableSet(); } Encapsulation problem: setStudents University dtu = new University("DTU"); .. Set<Student> students = new HashSet<Student>(); dtu.setStudents(students); • Providing a setBs (e.g. setStudents(aCollection)) method poses also encapsulation problems. Why? • Again the client can change the association without the university knowing about it Student hans = new Student("Hans"); students.add(hans); ... → Solution: Here setStudents should make a copy of the collection public void setStudents(Collection<Student> stds) { students = new HashSet<Student>(stds); } 19
  20. 20. Solution: How to change the association? * BA public class A { private Collection<B> bs = new java.util.HashSet<B>(); public void addB(B b) { bs.add(b);} public void contains(B b) { return bs.contains(b); } public void removeB(B b) { bs.remove(b); } } • addB, removeB, ...control the access to the association • The methods could have more intention revealing names, like registerStudent for addStudent – addB, removeB, ...would normally not shown in class diagrams – intention revealing methods like registerStudent would be shown in class diagrams 2.5 Bi-directional associations Bi-directional associations Person Firma * 0..1 * * ansatte kunde*barn 2forældre when associations don’t have any arrows, can this be understood • as bi-directional, i..e. navigable in both directions, where on has decided not to show navigability, e.g. – every person object has a reference to his employer – every company object has a reference to his employee • or as an under specification of navigability The example shows also • two different associations between person and companies • and a self-association Referential Integrity f1:Firmap1:Person arbejdsgiveransatte p2:Person f2:Firma arbejdsgiveransatte • Referential Integrity: 20
  21. 21. – For all employees of a company c, their company has to be c and – for all persons p,they have to employee of the company they are employed in arbejdsgiver f1:Firmap1:Person ansatte p2:Person f2:Firma ansatte arbejdsgiver Referential Integrity: setArbejdsgiver arbejdsgiver f1:Firmap1:Person ansatte p2:Person f2:Firma ansatte arbejdsgiver • setArbejdsgiver needs to ensure that the company is removed from the old employer and added to the new employer Referential Integrity: addAnsatte arbejdsgiver f1:Firmap1:Person p2:Person f2:Firma ansatte arbejdsgiver ansatte • addAnsatte needs to ensure that the old employer for the person is removed and set to the new new employer 21
  22. 22. Summary bi-directional associations • Use bi-directional associations only when necessary • Don’t rely on that the clients will do the bookkeeping for you 2.6 Generalisation Generalisation • Each instance of the specific classifier is also an indirect instance of the general classifier → Substitutability – One can use objects of the subclass instead of objects of the superclass → this is called the Liskov-Wing Substitution Principle ”If S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness).” • Achieved by inheritance – The structure (i.e. fields and operations) of the superclass are inherited by the subclass Generalisation Example Book Book(String,String,String) int fine int maxBorrowInDays {abstract} Medium String signature String title String author Calendar borrowDate Medium(String,String,String) int fine int maxBorrowInDays boolean isOverdue boolean isBorrowed Cd Cd(String,String,String) int fine int maxBorrowInDays fine and maxBorrowInDays are abstract in Medium and defined differently in Book and Cd. For Book we have 20 DKK and 28 days, while for CD we have 40 DKK fine and max days for borrowing is 7. Liskov-Wing Substitution Principle ”If S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness).” 2.7 Dependencies Dependencies 22
  23. 23. Dependencies • Dependencies show which class/association/... depends on which class/association/..., i.e., the class/association will change if the class/association it depends on changes. • In general, the notation is a broken line. • Possible stereotypes (or notational variants) indicate special types of dependencies Dependencies • UML defines keywords for special dependencies e.g. for classes call The source calls an operation in the target create The source creates instances of the target derive The source is derived from the target instantiate The source is an instance of the target permit The target allows the source to access the targets private features realize The source is an implementation of a specification or interface defined by the target refine Refinement indicates a relationship between different semantic levels substitute The source is substitutable for the target trace Used to track such things as requirements to classes or how changes in one model link to changes elsewhere use The requires the target for its implementation • but also for, e.g., use cases include A use case includes another use case extend A use case extends another use case at some extension point 23
  24. 24. 2.8 Interfaces Interfaces • Correspond to interfaces in Java • Define a contract that a class that realizes the interface has to fulfil • Interface descriptions in UML can contain operations and attributes Requires and implements interface dependency • As dependencies • Lollipop notation 24
  25. 25. 25

×