Lösungsvorschlag Software Architecture FS08
Inhaltsverzeichnis
Modularity, ADT, Designy by Contract and Concurrency (8 points)
b
d
f: true, since the visitor needs to know implementation details of the objects it visits
b: true because class invariants are 'and'-ed, hence stronger assertions can be introduced
c
Design by Contract (12 points)
<eiffel> class
BANK_ACCOUNT
feature withdraw(v: INTEGER) is
require TRUE
do end
feature balance: INTEGER
end
class STUDENT_ACCOUNT inherit BANK_ACCOUNT redefine withdraw end
feature withdraw(v: INTEGER) is require else balance >= v do
balance := balance - v
ensure
balance_altered: balance = old balance - v
end
end
class B_A_NORMAL inherit BANK_ACCOUNT redefine withdraw end
feature withdraw(v: INTEGER) is require else balance > v + fee do
balance := balance -v
ensure
balance_altered: balance = old balance - v
end
feature
fee: INTEGER
end
class B_A_BUSINESS inherit BANK_ACCOUNT redefine withdraw end
feature withdraw(v: INTEGER) is require else balance + credit > v do
balance := balance -v
ensure
balance_altered: balance = old balance - v
end
feature
credit: INTEGER
end </eiffel>
Abstract Data Types (16 points)
Writing and ADT for CREDIT_CARD (7 points)
TYPES
CREDIT_CARD
FUNCTIONS
Creators:
new_card: INTEGER x INTEGER --|--> CREDIT_CARD
Queries:
limit: CREDIT_CARD ----> INTEGER
balance: CREDIT_CARD ----> INTEGER
Commands:
settle: CREDIT_CARD ----> CREDIT_CARD
charge: CREDIT_CARD x INTEGER --|--> CREDIT_CARD
PRECONDITIONS
P1 new_card (limit: INTEGER) require limit > 0
P2 charge (c: CREDIT_CARD; amount: INTEGER) require amount + balance(c) <= limit(c) and amount > 0
AXIOMS
A1 limit(new_card(l: INTEGER)) = l
A2 balance(new_card(l: INTEGER)) = 0
A3 limit(settle(c: CREDIT_CARD)) = limit(c)
A4 balance(settle(c: CREDIT_CARD)) = 0
A5 limit(charge(c: CREDIT_CARD; a: INTEGER)) = limit(c: CREDIT_CARD)
A6 balance(charge(c: CREDIT_CARD; a: INTEGER)) = balance(c: CREDIT_CARD) + a
Proof of balance properties (6 points)
Prove that balance(c) >= 0:
Base case: using A2, new_card(l)
Induction assumption: is a correct expression of type CREDIT_CARD of length , i.e.
Induction step:
using A4
using A6, induction assumption for , and P2 for
Prove that
Base case:
new_card(l: INTEGER)
using A2
using A1
Hence: using P1 for the last inequality
Induction assumption:
is a correct expression of type CREDIT_CARD and length , that is:
and
Induction step (reuse inequalities found for balance above):
(see above) and using A3 and induction assumption, hence
using P2
using A5
since above we could show that a stronger statement was true
Proof of sufficient completeness (3 points)
We need to show that calling both query functions on any valid CREDIT_CARD expression will either return a value (different from a CREDIT_CARD), or a CREDIT_CARD expression which is one element shorter than the original one.
limit(new_card(l)) = l, using A1
balance(new_card(l)) = 0, using A2
Let be a valid expression of type CREDIT_CARD and length n, then:
limit(charge(c_n, a)) = limit(c_n) using A5
balance(charge(c_n,a)) = balance(c_n) + a using A6
limit(settle(c_n)) = limit(c_n) using A3
balance(settle(c_n)) = 0 using A4
Design Patterns I (26 points)
Identify patterns (12 points)
Bridge
CAR_FACTORY is the abstraction and CAR_FACTORY_IMP the implementation.
This is a structural pattern.
The bridge pattern observes separate abstraction and implementation inheritance trees. An abstraction has-a implementation object, and this client-supplier relation is referred to as the bridge. All methods in the deferred abstraction class are implemented in terms of the deferred implementation (the deferred classes being the topmost super classes in the corresponding hierarchies). Subtypes of the abstraction super class implement their methods in terms of the methods provided by said abstraction super class.
Abstract Factory
APPLICATION receives a factory object and CAR_FACTORY is the corresponding factory which makes the products desired by APPLICATION.
This is a creational pattern.
The deferred class CAR_FACTORY, or CAR_FACTORY_IMP provides an interface for the production of a complex product consisting of a number of other products (i.e. a car consists of wheels, the body, and an engine). Each of these subproducts (wheels, body, engine) come in different concrete implementation forms. The abstract factory pattern is used to implement specific versions of the interface provided by the deferred or abstract factory, where concrete versions of the abstract factory chose a specific concrete subproduct to be produced.
Builder
BMW_FACTORY_IMP has a concrete builder object and CAR_BUILDER is a generic class which implements the builder.
This is a creational pattern.
Just like the abstract factory pattern, the builder pattern is concerned with assembling complex products from a number of subproducts, and making specific decisions about the corresponding concrete (implemented) subproducts. Contrary to the abstract factory pattern, the builder pattern does not return the final complex product immediately. Instead, as a last step, the client of the builder (BMW_FACTORY_IMP in this case) will call the feature last_car on the corresponding builder in order to obtain the final product.
Build Mercedes cars (14 points)
<eiffel>
class MERCEDES_FACTORY_IMP
inherit CAR_FACTORY_IMP
feature {NONE}
make is -- blah do create sedan_builder create convertible_builder create station_builder end
feature
build_sedan is -- do sedan_builder.build last_card := sedan_builder.last_product end
build_convertible is -- do convertible_builder.build last_card := convertible_builder.last_product end
build_station is -- do station_builder.build last_card := station_builder.last_product end
feature
sedan_builder: CAR_BUILDER[MERCEDES_SEDAN_BODY, MERCEDES_ENGINE, MERCEDES_WHEEL] convertible_builder: CAR_BUILDER[MERCEDES_CONVERTIBLE_BODY, MERCEDES_ENGINE, MERCEDES_WHEEL] station_builder: CAR_BUILDER[MERCEDES_STATION_BODY, MERCEDES_ENGINE, MERCEDES_WHEEL]
end </eiffel>
Web shop (16 points)
Copy & Paste
The copy and paste approach is not a good solution.
First, SALES_ORDER_GERMANY is not a meaningful descendant of SALES_ORDER. One should abstract SALES_ORDER into a general class describing sales and order, and then inherit from this new abstraction in order to implement SALES_ORDER_... for Switzerland and Germany.
Case distinction
This approach is more meaningful than the copy & paste approach.
However, using this approach one ends up with a lot of case distinctions if the specification changes and more country-specific processes need to be carried out. Also, using object-oriented programming one is able circumvent this sort of case distinction. That is, using inheritance and polymorphism would be a better approach which would make use of the concepts that modern OO programming provides to us.
Good about this approach is that, assuming taxing is the only country-specific difference, it is quite short and requires little code.
A solution based on inheritance
This approach which utilizes inheritance and polymorphism is the modern approach implied by OO technologies.
Problems may arise if one assigns dynamically an erroneous object reference, and begins to carry out country-specific operations not applicable to the actual country?
A design pattern might help
I would utilize the abstract factory pattern.
There should be deferred classes FORM, TAXES, PROCESS_ORDER, and RECEIPT. All of these classes define interfaces with the obvious meanings: FORM defines the interface of the web shop form to be filled out etc.
One can then implement country-specific concrete classes of these deferred classes, i.e. FORM_CH, FORM_DE, TAXES_CH, TAXES_DE, etc.
A deferred class ORDER_FACTORY defines the interface of the notion of receiving an order via the web form, processing it, taxing it, etc., and shipping it to the customer.
One can now implement concrete factories ORDER_FACTORY_CH, and ORDER_FACTORY_DE which implement the corresponding interface, using the country specific products of type FORM, TAXES, etc.
The actual shops in Switzerland and Germany will now receive an instance of the corresponding concrete factory, and can use the corresponding public interfaces to receive, process, and ship orders.
Visitor & Composite Pattern (16 points)
Theoretical Questions (6 points ??)
Pattern Categories (2 points)
Composite pattern: Structural pattern
Visitor pattern: Behavioural pattern
Visitor and open-closed principle (2 points)
In the visitor pattern you always have a deferred visitor class which provides the necessary interface, and a number of concrete implementations of said interface which implement the deferred methods in a specific way.
This pattern adheres to the visitor pattern since it consists of an abstract interface which is closed to modifications (except for error correction), and one inherits from this abstract interface in order to change the behavior. One does not change the interface.
Thus the deferred visitor (or abstract interface) is closed to modification but can be inherited from and changed (i.e. it's open too).
Class Diagrams (2 points)
Pattern Implementation (10 points)
Accept (3 points)
<eiffel>
class COMPOISITE_FILE inherit COMPONENT
...
feature accept(a_visitor: VISITOR) is -- blah do a_visitor.visit_file(Current)
end
</eiffel>
<eiffel>
class COMPOISITE_FOLDER inherit COMPONENT
...
feature accept(a_visitor: VISITOR) is -- blah do a_visitor.visit_folder(Current)
end
</eiffel>
Visit (7 points)
<eiffel> class VISITOR_CHECK_CHANGE inherit VISITOR
...
feature visit_folder (a_folder: COMPOSITE_FOLDER) is -- blah do from a_folder.children.start until a_folder.children.after loop a_folder.children.item.accept(Current) a_folder.children.forth end end
visit_file (a_file: COMPOSITE_FILE) is -- blah do if a_file.changed then io.put_string(a_file.name) io.new_line end end </eiffel>