1.34k likes | 1.47k Views
Workshop: Moderne Softwareentwicklungs-Methoden Teil 1: AOP, MDSOC, GP, IP Teil 2: eXtreme Programming Markus Völter markus.voelter@mathema.de Michael Wiedeking michael.wiedeking@mathema.de. Contents (I). The big picture Product Line Engineering Variability Analysis
E N D
Workshop:Moderne Softwareentwicklungs-MethodenTeil 1: AOP, MDSOC, GP, IPTeil 2: eXtreme ProgrammingMarkus Völter markus.voelter@mathema.deMichael Wiedeking michael.wiedeking@mathema.de
Contents (I) • The big picture • Product Line Engineering • Variability Analysis • How AOP, GP, IP and MDSOC can help • Generative Programming • Principles • Compile-time Metaprogramming (using templates) • Jenerator – Generative Programming for Java • Other GP tools • Aspect-Oriented Programming • Principles • Patterns for AOP • AspectJ • Sally • Jenerator and AOP
Contents (II) • Multidimensional Separation of Concerns • Problem revisited: Multidimensional Variability • Example introduced: SEE • Hyper/J concepts • Example continued • Intentional Programming • Background • Basic Concepts • IP System Architecture • Active Source • Stunning Features • Thanks • Resources
The big pictureProduct Line EngineeringVariability AnalysisHow AOP, GP, IP and MDSOC can help
Common Roots: Product Line Engineering • Instead of developing one single system, development focuses on a family of related systems.Reasons: • different target platforms (operating system, middleware) • different quality of service (QoS) requirements, • potential for future growth • Systems are built from product-specific parts and reusable part: the common platform • Creation of concrete systems should be done with the help of a (semi-)automatic process • industrialized production of software • „build software like they build cars“
Common Roots: Product Line Engineering Software Familyas defined by Parnas in 1976: We consider a set of programs to constitute a family whenever it is worthwhile to study programs from the set by first studying the common properties of the set and then determining the special properties of the individual family members. • Instead of developing one single system, development focuses on a family of related systems.Reasons: • different target platforms (operating system, middleware) • different quality of service (QoS) requirements, • potential for future growth • Creation of concrete systems should be done with the help of a (semi-)automatic process • industrialized production of software • „build software like they build cars“
Product Line Engineering: Process • Domain Scoping • Variability Analysis • Domain Structuring • Define common architecture • Define Production Plan • Define Building Blocks • Components • DSLs & Generators • Production Process
Product Line Engineering: Process • AOP, IP, GP & MDSOC are located here. But first...
Domain Analysis • Goal: A domain model • Scoping defines, what is part of the domain, and what is not (defines the range of possible products) • Example: GQM (Goal Question Metric) • A common vocabulary defines the terms in which the domain can be described • The commonalities and the differences between different products in the domain have to be definedVariability Analysis
Variability Analysis • Variability analysis discovers the variable and fixed parts of a product in a domain. Parts can be • structural, or • behavioral (functional) • non-functional (technical) • To define variable aspects, we need to have a commonality base: something fixed • There are two kinds of variability: • positive variability: add something (optional) • negative variability: removes something (essential) • Positive variability leaves the concept intact, while netative variability does not.
Domain Analysis: Feature Modelling • Feature Modelling is more abstract than OO analysis and design because it does not prescribe the realization of features • A feature model describes the (sub-)features of a concept • Subfeatures can be • Mandatory • Optional • Alternative • N of M • A feature model can be multi-dimensional • A graphical notation exists: feature diagrams
Feature Diagram: Aircraft example • Example products: • An aircraft with a low wing, piston engineand made of metal, wood and cloth: Robin DR-400 • An aircraft with shoulder wing, no engineand made of plastic: ASW-27 • An aircraft with low wing, jet engine(s)and made of metal: Airbus A320
More features of feature diagrams • They can contain constraints on the combinations of features • They can define „names“ for specific combinations of features; feature groups • Features can be open: additional subfeatures can be added • Features can be incomplete: the subfeatures are not yet defined • Multiplicity of subfeatures can be added
Exercise 1: Feature Models Describe a feature model for a specific concept using a feature diagram. You may use a suitable editor for that purpose. Possible concepts: Personal Computer, Vehicle, JVM, curriculum, list (software)
Binding Times • A feature diagram defines the common and variable aspects of a system. It has to be defined, when the variable aspects are fixed for a particular product.This is called binding time. • Usually, binding time has consequences on • flexibility • performance • code size • type safety • and: on the technique used to implement the variable aspect
Typical Binding Times • source time: manual programming, template parameters • compile time: function overloading, precompiler, template evaluation • link time: DLLs, class loading • run time: virtual functions, inheritance & polymorphism, factory-based instance creation, delegation • deployment/configuration time: component deployment (impl. for an interface), environment variables
Exercise 2: Binding Times Depending on the programming language used, there are different techniques how the binding times can be realised. Some may even be impossible. Select one of the following languages and describe the possible realization of the binding times, as well as potential advantages and drawbacks: Languages: C++, Java, Eiffel, Ada, Smalltalk, Perl
Source Time binding • Source time binding is tedious and error prone, because it has to be programmed manually. • However, it has significant advantages: High performance, low code size • Of course, some flexibility is sacrificed • To make source time binding more efficiently usable, generative programming can be used.
Multi-Dimensional Variability • A software system, and a family of systems in a domain, is usually decomposed, or structured using one dimension only.
What is multi-dimensional variability • Multi-Dimensional means: • The variability aspects can vary independently • They cannot be decomposed in one hierarchy • Typically, the result is many possible combinations (permutations) of the dimensions
Multi-Dimensional Variability • What, if an certain feature cannot be decomposed along this same dimension?
Multi-Dimensional Variability: Apache Example • Logging • SessionExpiration
Multi-Dimensional Variability • Things that are tangled all through the primary decomposition structure are called cross-cutting concerns. • have a well-defined purpose • have a non-obvious structure • Thus, Aspects are a way to separate concerns. It • catches cross-cutting concerns • in a single syntactic entity
AOP generalized • AOP uses one primary decomposition structure (classes) and several additional aspects that cross-cut this structure. • New aspects can be added after the system is designed • A more generalized approach to realizing multi-dimensional variability is Multi-Dimensional Separation of Concerns (MDSOC): • There is no primary decomposition structure, all dimensions are treated equal. • each concerns is specified in its decomposition structure, • and combined with other concerns in a second step.
MDSOC-Techniques • Help to modularize the different concerns (dimensions) that arise from the feature model of a product family. • Examples of MDSOC are • adaptive programming • aspect oriented programming (AOP) • composition filters • role-modelling • subject-oriented programming (SOP) • hyperspaces
Specifying products • A product in a software family can be specified by combining the features in a specific, legal way. • Such a specification can become quite complex, because • Several different techniques might be used • Constraints on the feature model have to be enforced • It might be very low-level (programming-language dependent) • To overcome these problems, Domain-Specific Languages can be used.
Domain-Specific Languages (DSLs) • Allow to specify systems (or products of a family) in terms (vocabulary) of the software family. A higher level of expressiveness is thus reached. • Thus, Intentional Programming can be seen as a construction kit for multilevel-DSLs: • represents general and domain specific abstractions as intentions • provides an extensible environment that allows to load (domain-specific) extensions • represents source code as active entities • extensions can affect the complete environment; the editor, compiler, debugger, version control, ...
? ? Domain Engineering: Can it be used today? • It has been in use by several institutions, e.g. SEI, Mobile Phone manufacturers • Feature models and diagrams are useable today and very helpful, so is variability analysis. • Frameworks are a way to realize product lines, as well as components. Both are in use today. • Thinking about binding times other than runtime is also very useful. Ok, this is not really part of the tutorial, but anyway
Generative ProgrammingPrinciplesCompile-time Metaprogramming Jenerator – Generative Programming for JavaOther GP tools
Generative Programming (GP) • Industrialized production of products from a product line/system family • Take care of the different binding times by using appropriate tools • For example code generation to take care of source time variability, • Reflection at runtime • Optimization wherever possible and useful • Improve performance by remove runtime decisions • Reduce code size by only including the really needed parts
GP Elements • Generative Domain Model consists of the following elements • A way to specify products(family members) • Implementation componentsfrom which these members can be assembled • And configuration knowledge, which maps between the spec of a member and its implementation • The terms problem space and solution space are used for specification and implementation components
Product Specification • Product specification is usually done by using • feature models (in textual or graphical notation) • declarative languages (DSLs) • regular programming with generators
Configuration Knowledge • Constraints on the configuration space, i.e. legal and illegal product configurations • Defaults configurations (default components and dependencies) • Feature mapping (which implementation components satisfy which feature) • Optimizations (optimzing for performance, or for code size, or for safety)
Implementation Components • Active Libraries (such as template libraries that generate code) • DSLs and their mappings • code generators • code transformers
GP Example: C++ Template Metaprogramming • Also called compile-time metaprogramming, because metaprograms „run“ while the program is compiled • Uses the features of C++ template instantiation • Programming style is „functional“ and operates on types • Note that some awkward constructs are required, • because C++ templates were not originally intended for this purpose • and many generally unknown and non-trivial features of the standard are used. • Error reporting is usually clumsy
GP Example: C++ Template Metaprogramming struct EmptyBoundsChecker { static void checkBounds(const int& i,const int& length) { } }; //... int main() { Vector<int> v1;//v1 uses SimpleBoundsChecker Vector<int, EmptyBoundsChecker> v2;//v2 uses EmptyBoundsChecker return 0; } struct SimpleBoundsChecker; template<class ElementType, class BoundsChecker=SimpleBoundsChecker> class Vector { public: //... ElementType& at(const int& i) { BoundsChecker::checkBounds(i,length); //... } //... }; struct OutOfBounds {}; struct SimpleBoundsChecker { static void checkBounds(const int& i,const int& length) { if(i<0 || i>=length) throw OutOfBounds(); } };
GP Example: C++ Template Metaprogramming struct EmptyBoundsChecker { static void checkBounds(const int& i,const int& length) { } }; //... int main() { Vector<int> v1;//v1 uses SimpleBoundsChecker Vector<int, EmptyBoundsChecker> v2;//v2 uses EmptyBoundsChecker return 0; } • „combines“ the classes at compile time • Creates only those versions that are really used • Inlining is used to improve runtime performance (or reduce code size) • Calls to empty operations are eliminated struct SimpleBoundsChecker; template<class ElementType, class BoundsChecker=SimpleBoundsChecker> class Vector { public: //... ElementType& at(const int& i) { BoundsChecker::checkBounds(i,length); //... } //... }; struct OutOfBounds {}; struct SimpleBoundsChecker { static void checkBounds(const int& i,const int& length) { if(i<0 || i>=length) throw OutOfBounds(); } };
Template Metaprogramming: IF #include <iostream> using namespace std; template<bool condition, class Then, class Else> struct IF { typedef Then RET; }; //specialization for condition==false template<class Then, class Else> struct IF<false, Then, Else> { typedef Else RET; }; void main() { cout << "sizeof(short) = " << sizeof(short) << endl << "sizeof(int) = " << sizeof(int) << endl << "sizeof(IF<(1+2>4), short, int>::RET) = " << sizeof(IF<(1+2>4), short, int>::RET) << endl; IF<(1+2>4), short, int>::RET i; //the type of i is int! } • A static if can be used to check boolean conditions on types at compile time.
Compile Time Factorial #include <iostream> using namespace std; #include "../meta/meta.h" using namespace meta; struct Stop { enum { RET = 1 }; }; template<int n> struct Factorial { typedef IF<n==0, Stop, Factorial<n-1> >::RET PreviousFactorial; enum { RET = (n==0) ? PreviousFactorial::RET : PreviousFactorial::RET * n }; }; void main() { cout << Factorial<3>::RET << endl; } • Statically calculatesthe factorial of anint at compile time! • At runtime, the resultis a constant!
Other compile time functions • Static versions of • switch, • while, • do-while • for • etc... • It has been shown that the C++ template facility can be used as a full-blown functional programming language. Its data are the types and constant integral values of the C++ language. • In generative programming, these and other constructs are used to select code to be included in the final program.
Exercise 3: Template Metaprogramming #include <iostream> using namespace std; #include "../meta/meta.h" using meta::IF; const int DEFAULT = ~(~0u >> 1); //initialize with smallest struct NilCase {}; template <int tag_, class Type_, class Next_ = NilCase> struct CASE { enum { tag = tag_ }; typedef Type_ Type; typedef Next_ Next; }; Below you can find a static version of switch. Try(!) to explain how it works. // SWITCH<> template<int tag, class Case> class SWITCH { typedef typename Case::Next NextCase; enum { caseTag = Case::tag, found = (caseTag == tag || caseTag == DEFAULT) }; public: typedef IF<found, typename Case::Type, typename SWITCH<tag, NextCase>::RET >::RET RET; }; template<int tag> class SWITCH<tag, NilCase> {public: typedef NilCase RET; }; // test struct A {static void exec(){ cout << "A" << endl;} }; struct B {static void exec(){ cout << "B" << endl;} }; struct D {static void exec(){ cout << "Def" << endl;}}; void main() { SWITCH<(1+1-2), CASE<1,A, CASE<2,B, CASE<DEFAULT,D > > > > ::RET::exec(); }
Why use template metaprogramming • Allows you to „modify“ or adapt your program at compile time to achieve • Performance Optimizations, • Inlining, • Loop unrolling • Size-specific adaptions • Type-specific adaptions • Memory Optimizations • Code size Optimizations • Keep out all the code that is not necessary • Use code optimized for specific cases • Other stuff • Adapt Interfaces at compile time • Impress and scare other people
Example: Generic Container public: PtrList(SetHeadElementType& h, ReturnType *t = 0) : head_(0), tail_(t) { setHead(h); } ~PtrList() { Destroyer::destroy(head_); } void setHead(SetHeadElementType& h) { TypeChecker::check(h); head_ = Copier::copy(h); } ElementType& head() { return *head_; } void setTail(ReturnType *t) { tail_ = t; } ReturnType *tail() const { return tail_; } private: ElementType* head_; ReturnType *tail_; }; template<class Generator> class PtrList { public: //make Config available as a member type typedef typename Generator::Config Config; private: typedef typename Config::ElementType ElementType; typedef typename Config::SetHeadElementType SetHeadElementType; typedef typename Config::ReturnType ReturnType; typedef typename Config::Destroyer Destroyer; typedef typename Config::TypeChecker TypeChecker; typedef typename Config::Copier Copier;
Example: Generic Container public: PtrList(SetHeadElementType& h, ReturnType *t = 0) : head_(0), tail_(t) { setHead(h); } ~PtrList() { Destroyer::destroy(head_); } void setHead(SetHeadElementType& h) { TypeChecker::check(h); head_ = Copier::copy(h); } ElementType& head() { return *head_; } void setTail(ReturnType *t) { tail_ = t; } ReturnType *tail() const { return tail_; } private: ElementType* head_; ReturnType *tail_; }; • specific specialized templated classes exist for Copier, TypeChecker and Destroyer (kind of static subclassing) • Config serves as the configuration repository and defines, which of them is used in a particular generation template<class Generator> class PtrList { public: //make Config available as a member type typedef typename Generator::Config Config; private: typedef typename Config::ElementType ElementType; typedef typename Config::SetHeadElementType SetHeadElementType; typedef typename Config::ReturnType ReturnType; typedef typename Config::Destroyer Destroyer; typedef typename Config::TypeChecker TypeChecker; typedef typename Config::Copier Copier;
Example: Generic Container • Config serves as the configuration repository for a specific List instantiation struct RefPolyPersonListConfig { typedef Person ElementType; typedef ElementType SetHeadElementType; typedef EmptyDestroyer<ElementType> Destroyer; typedef EmptyTypeChecker<ElementType> TypeChecker; typedef EmptyCopier<ElementType> Copier; typedef PtrList<RefPolyPersonListConfig> ReturnType; } // use it! Typedef RefPolyPersonListConfig::ReturnType PersonList; PersonList list = new PersonList();
Example: Generic Container • Last step: A Generator that takes a config structure and returns the generated product. • It uses enums, static IFs and static SWITCHes heavily. • Provides useful defaults • Usage example: LIST_GEN< Person, ext_ref, poly >::RET someList;
Example: Generative Programming in Java • Java provides no templates. Template metaprogramming thus cannot work! • Also, GJ or Java 1.5 templates or Ada can‘t do it, because they are implemented differently. • One approach is Jenerator, a code generation framework in and for Java. • It is implemented by MATHEMA and used in several projects • EJB generation • Component Container generation • ...