1  Object Oriented Paradigm (Draft)

We ough to understand how we move from the procedural programming approach to an object-oriented approach, that is, understanding the motivations and the fundamental characteristics of the object-oriented paradigm.

1.1 Object Oriented Paradigm

There are several programming paradigms, i.e. the model of thinking about programs. A paradigm defines how computation is structured, how state and control flow are handled, and what abstractions can be used. In prinicple you can adopt a paradigm event if the language is not designed for it, but such task requires a much larger effort than when using a language that supports the required paradigm.

The main programming paradigm are:

  • Procedural (Pascal, C,…)
  • Object-Oriented (C++, Java, C#,…)
  • Functional (LISP, Haskell, SQL,…)
  • Logic (Prolog)

Many languages are multi-paradigm, allowing a mix of styles. Historically, new programming paradigms have emerged together with languages offering support for such paradigms, thus providing proof of concept for the paradigm.

timeline
    '60 : 1st Gen HLL<br>Fortran<br>COBOL<br>Algol60
        : OO<br>Simula67
    '70 : 2nd Gen HLL<br>Algol68<br>Pascal<br>C
        : OO<br>Smalltalk
    '80 : 3rd Gen HLL<br>Ada<br>Modula2
        : OO<br>C++<br>Eiffel<br>Ada9x
    '90 : -
        : OO<br>Python<br>Java
Figure 1.1: Languages timeline

The first high-level programming languages – mainly Fortran and COBOL in the 1960s – were procedural. Precisely because they were high-level languages, they allowed you to define variables and structured data types.

Later, toward the late 1960s and early 1970s, a second generation of high-level procedural languages emerged: C is the main example, but there were also Pascal and ALGOL 68. These introduced structured programming(Dijkstra 1968) -— block-structured control constructs -— and more structured data typing, with conversions and compatibility checks among variable types when used together.

Subsequently, in the late 1970s and early 1980s, further high-level procedural languages appeared—Ada among the best-known—which defined the notions of modules, concurrent programming, and exceptions. In parallel with this main procedural line, there was also a series of object-oriented languages. The first was Simula 67 (as the name suggests, developed in 1967), the first programming language that truly had the main constructs we now associate with object-oriented languages. Later came Smalltalk in the early 1970s, which saw significant use. Smalltalk had some limitations, especially in terms of efficiency, but it helped spread the idea and approach of object-oriented programming.

Finally, in the 1980s, C++ emerged, along with a couple of other languages (such as Eiffel) and Ada’s evolution (from an initially procedural language). In the 1990s, a second generation of object-oriented languages appeared; Java is the main one, but we also have others like Python, and then toward the late 1990s C#, a fairly widespread object-oriented language.

The object-oriented approach introduces the concept of object. It is a new language-level construct (i.e. something that is part of the language syntax) that groups together the data and the functions that operate on them. An object is an entity with an interface -— the set of operations that it accepts and that correspond to some function in the object – and a state – the values stored in the data is contains – that can be changed by the functions.

A fundamental aspect of objects is that they hide data. Information hiding is the principle of hiding data inside an object so that it can be seen and modified only by the functions inside that object.

1.1.1 Object and Classes

The fundamental idea of the object-oriented approach is that we define objects representing well-known domain concepts with a clear role in the problem domain. Beyond objects, the object-oriented approach includes classes.

It is important to understand that when dealing with OO development, there are two distinct abstraction levels that we can call abstract level and concrete level. At the abstract level we talk about concepts, entities, categories, types; at the concrete level we talk about instances, elements, objects, examples, occurrences. OO moves between these two with the terms class (abstract, design-time) and object (concrete, runtime). At design time we define classes; at runtime we observe objects interacting, containing data, and operating on their own data.

Level Examples
Abstract Concept
Entity
Class
Category
Type
Concrete Instance
Item
Object
Example
Occurrence

It is a common case to have many similar objects embodying the same idea (e.g., product). A class defines the common characteristics of all objects of that kind, it constitutes a higher-level concept with respect to object. The passage linking class and object is called instantiation: a class can be instantiated to create objects, and all objects instantiated from the same class share the same characteristics. E.g., for a Product class, common features might be having a name and a price. Objects are created – at run-time – as instances of classes – defined at compile-time –. The class defines the structure of an object an therefore it represents the type of the object. A class defines how an object is built through a constructor function and initialization is performed automatically by the language.

A class declaration is also a type definition, typically no memory is allocated until an object is created from the class.

The creation of an object is called instantiation. The created object is often called an instance of the class. There is no limit to the number of objects that can be created from a class. Each object is independent and has its own identity, thus interacting with one object doesn’t affect the others.

Once a class is identified to represent a specific concept, its structure – how its instances are laid out – must be detailed. The core elements in a class are the attributes (just like variables in any language: they have a name and a type). The class defines the names and types; each object holds values for each attribute. The set of values that are associated to the attributes if an object constitute the state of an object.

Class vs. Object:

  • Class (the description of object structure, i.e. type):
    • Data structure (attributes or fields or properties)
    • Behavior (methods or operations)
    • Creation methods (constructors)
  • Object (class instance)
    • State and identity

An engineering approach would require that, given a system, with components and relationships among them, we shall: first identify the components, then define the component interfaces, define how components interact with each other through their interfaces. And in general minimize relationships among components.

The interface of a component is composed of the set of messages that an object can receive. Each message is mapped to an internal procedure within the object. The object is responsible for the association (message -> function). Any other message is illegaland results in an error.

The interface encapsulates the internals and exposes a standard boundary through with interactio with other objects occurr. The interface of an object is simply the subset of methods that other “program parts” are allowed to interact with.

Encapsulation lead to several benefits:

  • simplified access: to use an object, the user need only comprehend just the interface. No knowledge of the internals are necessary;
  • self-containmen: once the interface is defined, the programmer can implement the interface (i.e. define the the object internals) without interference from others;
  • ease of evolution: implementation can change at a later time without affecting any other part of the program (as long as the interface doesn’t change)
  • single point of change: any change in the data structure means modifying the code in one location, rather than code scattered around the program (which is error prone)

Classes can also be related to each other: for instance a receipt contains items/lines; each item refers to a product; products are contained in a price list.

1.1.2 Inheritance

In OOP, classes can also support inheritance: from a base class you can define a more specific class that inherits all the features of the base class with a sort of virtual copy-and-paste.

Inheritance enables two important related features: - polimorphism is the ability to manage uniformly different object types that are able to respond to the same operation in their own specific way. - dynamic binding is the process of determining at runtime which method implementation to invoke based on the actual type of the object, i.e. determining the specific way an object respond to an operation.

1.1.3 OO Summary

In summary classes

  • represent high level concepts, often taken from problem domain;
  • are instantiated into Objects;
  • define common features of Objects;
  • can be related to each other, thus defining links and communication patterns among their instances (i.e. objects);
  • can be defined through inheritance, where specific classes inherit from general ones.

The adoption of the object-oriented approach was motivated by programs getting too large to be fully comprehensible by any person. A requirement emerged for a way of managing very-large projects. Thus the Object Oriented (OO) paradigm allows programmers to (re)use large blocks of code without knowing all the picture. OO makes code reuse a real possibility and simplifies maintenance and evolution. Remember that most software cost is not in initial development but in maintenance, therefore any approach that reduces maintenance is a major advantage.

The actual benefits of adopting OO only occur in larger programs. Analogous to structured programming: for programs with e.g. 30 lines of codde, spaghetti is as understandable and faster to write than a well structured OO code. While for programs with thousands of lines of code, spaghetti is incomprehensible, probably doesn’t work, and it is not maintainable. Only programs with more than a few hundreds lines of code really benefit from OO.

Historically, object-oriented language construction evolved untile they achieve what we call today the object oriented paradigm.

  • The earliest were object-based (e.g. Ada): you could build an object as a set of related data and functions, but without the notion of class as a single definition for all objects sharing the same structure.

  • The next step class based (e.g., CLU) introduced classes, separating the high-level design concept from the low-level runtime entities.

  • The following step gave us fully object-oriented languages (e.g., Simula, Python) where objects come from classes and classes can inherit from each other, adding useful properties.

  • A subset are strongly typed OO languages (C++, Java), where data types are precisely defined and the compiler can perform many consistency checks.

    Think of C and Java: every variable has a well-defined type. (C is strongly typed though not object-oriented; Java is both.) Other languages, like Python, are dynamically typed: you define variables without fixed types; a variable can hold values of different types at different times; this prevents the compiler from doing certain checks; inconsistencies surface at runtime instead of compile time. In general, strong typing lets us catch potential errors earlier.

1.2 Example

To better understand what it means to develop in a procedural style versus an object-oriented style we can consider an simple example concerning a cash register.

1.2.1 Case study: cash register

The requirements requirements for the (simplified) cash register software are:

A cash register emits purchase receipts. A receipt is made up of a list of items. Each item corresponds to a product that has a name and a price. The information about the products is stored in a price list.

Any time a new product code is entered the corresponding item is added to the receipt. After the last item is entered, a list of all items (with product name and price) is printed, together with the total sum.

Example of interaction with the cash register

Input:

113
57
123
20
32
410
1
product codes
2
the 0 at the end represents the end of items and signals the list of items is complete
3
the type of payment (2 = cash)
4
the amount of cash received (10€)

Output:

Receipt:
    Banana: €   0.99
     Pasta: €   4.10
     Bread: €   3.49
--------------------
No items: 3

     Total: €   8.58
 
Cash provided: €  10.00
         Rest: €   3.53

1.2.2 Procedural solution

Let us consider a procudural implementation in C. First of all we need to think about what data structures and functions are needed to implement the program that.

Concerning the data structures, we need:

  • for the price list: an array of numbers -— the prices —- paired with an array of strings – the product names -—; let’s assume, for simplicity, a product’s code is the same as its index in these arrays.
  • for the items: an array of integers containing the product codes that represent the items included in the receipt, and a count of how many items are there on the receipt.
  • for the payment: a structu

As far as the operations (functions) are concerned, what is needed is essentially:

  • a function to read the codes from the cashier,
  • a function to add product codes to the receipt;
  • a function to process the payment;
  • a function to print the receipt at the end;
  • a function to initialize everything.

We also need a main function that initializes, then loops reading codes until it gets a special code that marks the end of the receipt, adding all codes; once the last product is reached, it prints the receipt — i.e., what we see on the printout.

So here is the draft of a possible solution, without the code details:

float prices[MAX_LIST];
char* names[MAX_LIST];
int receipt[MAX_RCPT];
int n_items;
struct {
   int type;
   double amount;
   union {
      struct { int card_type; } credit;
      struct { double cash, rest;  } cash;
    };
} payment;
void add(int); /* add an item to receipt */ 
void print(); /* print receipt */
void init(); /* initialize */
int read(); /* read the next item code */
void process_payment(); /* process payment */
int main(){
   init();
   int code;
   while( (code = read()) ){ add(code); }
   print();
}

There are several limitations in this solution:

  • there is no any intrinsic link between the two arrays -— prices and names –. As programmers we consider them to be “parallel” so we give them the same size, but there’s no guarantee enforced by the language;
  • there is no link between the item count (n_items) and the receipt array, in practive there is no control over size, apart from the code that we explicitly write;
  • it is not clear which function is allowed to operate on the data: essentially the functions add, print, and init. The receipt, item count, and the functions are strongly related to each other, but in the code we don’t see this relationship. There’s nothing in the language syntax to make them exist together as a single cohesive unit.
  • looking at main, we can observe that it calls init at the start. If one developer wrote the functions and data, and someone else used them, how can we be sure initialization happens correctly before the rest of the code uses the data, it could be in the documentation but we know real programmers don’t write documentation.

The above solution can be modeled with a diagram that represents functions (in purple) with arrows indicating calls among them, and data (in white) that the various functions read or write. There’s clearly no construct that binds these three functions and two data structures into a single cohesive unit.

G main() main() read() read() main()--read() add() add() main()--add() print() print() main()--print() init() init() main()--init() receipt receipt add()--receipt n_items n_items add()--n_items print()--receipt print()--n_items prices prices print()--prices names names print()--names init()--n_items init()--prices init()--names
Figure 1.2: Elements of a procedural solution and their relations

All these details limit the approach and are sources of potential errors when we write a program procedurally.

1.2.3 Object-Oriented Solution

We can apply the principle of object-orientation to migrate the procedural solution into an OO solution.

First of all we can apply the principle of encapsulation.

Bringing together together related code and data, i.e. init() + add() + print() + receipt + n_itemsand ecapsulating them into an object called Receipt can be modeled as represente in Figure 1.3.

G cluster_class Receipt                                                cluster_if            Interface main() main() read() read() main()--read() add() add() main()--add() print() print() main()--print() init() init() main()--init() receipt receipt add()--receipt n_items n_items add()--n_items print()--receipt print()--n_items prices prices print()--prices names names print()--names init()--n_items init()--prices init()--names
Figure 1.3: Elements of a procedural solution and their relations

You can’t arbitrarily add a new function elsewhere that accesses those data, bypassing the three main functions (add/print/init) that are meant to manage them.

Obviously, we can identify other correlated chunks of data. Besides the receipt + item count + three methods, we could have another object that groups the price list/catalog.

It is possible go further, identifying semantically consistent elements that correspond to the concepts in our problem domain. When discussing receipts with a shopkeeper, we’ll talk about products, price list, receipts, and lines/items on the receipt. So we might model receipt items, products (not just a single array, but a pair of data: price and name). Having an object that represents that pair and keeps it together overcomes one of the limits of the procedural approach. The result could be modeled as shown in Figure 1.4.

G cluster_price Pricelist cluster_class Receipt                                                cluster_if Interface           main() main() read() read() main()--read() add() add() main()--add() print() print() main()--print() init() init() main()--init() receipt receipt Item1 Item2 .. Item_n add()--receipt n_items n_items add()--n_items print()--receipt print()--n_items prices name1 price1 name2 price2 .. name_n price_n print()--prices init()--n_items init()--prices external() external() external()--main() external()--n_items X
Figure 1.4: Elements of a procedural solution and their relations

The object-oriented structure of our cash register software can be represented as shown in Figure 1.5, using a notation called UML Class Diagra:

classDiagram
    direction LR
    Receipt --> PriceList : is related to
    Receipt --> Item : contains several
    PriceList --> Product : contains several
    Item --> Product : refers to    
Figure 1.5: Class diagram of the cash register

In our example we might identify four classes: Receipt, PriceList, Item/Line, and Product.Receipt is linked to PriceList; changing the price list changes values in the receipt. The receipt contains multiple items; each item refers to a product; products are listed in the price list. We’ve thus moved from a low-level, detail-heavy structure (with things like MAX_LIST and MAX_RECEIPT, meaningful yet not immediately readable) to a higher-level view with classes. From these classes our program will have objects: one (or more) Receipt, one PriceList, many Items, many Products.

For instance, the above example can be extended with the payment for a specific receipt where the payment can be either cash or by credit card. A possible diagram for such exension can be modeled as showin in Figure 1.6.

The class Payment represents a general abstraction of payment, while the two derived classes Cash and Card represent a concrete specialization of that concept. The relation “paid” between Receipt and Payment means that – by virtue of polymorphism – any receipt can be linked to either a cash or card payment indifferently.

classDiagram
    direction TB
    Receipt --> PriceList : is related to
    Receipt --> Item : contains several
    PriceList --> Product : contains several
    Item --> Product : refers to    
    Payment <|-- Cash
    Payment <|-- Card
    Receipt --> Payment : paid
Figure 1.6: Class diagram for payment

A more detailed UML model of such a solution can be described as follows.

classDiagram
direction TB
class Receipt {
    + add(code: int): void
    + print(): void
}
class Item {
    + getPrice(): double
    + print(): void
}
class Pricelist {
    + findCode(code int): Product
}
class Product {
    - price: double
    - name: String
    + getPrice(): double
    + print(): void
}
class Payment {
    - amount: double
}
class Cash {
    - received: double
    - change: double
}
class Card {
    - cardType: enum Cards
}
Receipt "1" o-- "0..*" Item: contains
Receipt "0..*" --> "1" Pricelist: based_on
Pricelist "1" o-- "0..*" Product
Item "0..*" --> "1" Product : refers_to
Receipt "*" --> "0..1" Payment : paid
Payment <|-- Cash
Payment <|-- Card
Figure 1.7: Example of multiplicity in UML

1.3 UML and OO design

UML logo

To design a program’s structure, given a problem, we first identify the classes (which will become Java code — or code in any OO language) that solve it. To this end, the most commong tool is a graphical notation called UML (Unified Modeling Language).

It is a standardized modeling and specification language defined and managed by the Object Management Group (OMG) used to design software with the OO approach. UML provides a graphical notation to specify, visualize, construct, and document an object-oriented system. UML was born as an integration of the concepts of three main methods: Booch (Booch 1993), OMT (Rumbaugh et al. 1994) and RTOOM(Selic, Gullekson, and Ward 1994); it merged them into a single, common and widely used modeling language.

UML defines several diagrams that cover different aspects of software modeling an documentation:

  • Class diagrams
  • Activity diagrams
  • Use Case diagrams
  • Sequence diagrams
  • Statecharts

1.3.1 UML Class Diagram

The key diagram in OO development is the Class Diagram, it captures:

  • Main (abstract) concepts
  • Characteristics of the concepts
  • Data associated to the concepts
  • Relationships between concepts
  • Behavior of classes

1.3.2 Classes and objects

A Class: represents a set of objects, e.g. facts, things, people, they define

  • the common properties – also known as attributes or fields –, i.e. the data they contain,
  • the common operations – also called methods – 0they can perform.

An instance of a class is an object, the class represents the type of the object.

Example: in an application for a commercial organization City, Department, Employee, and Sale are typical classes.

Classes in UML are represented in UML as rectangles. They are internally divided into three compartments:

  • the top one contains the name of the class
  • the middle one contains the properties / attributes
  • the bottom one contains the operations / methods

classDiagram
    class Employee
    class City
    class Sale
    class Department

An Object is a model of a physical or logical item ex.: a student, an exam, a window. It is characterized by:

  • an identity that distinguish it from any other object,
  • the state that is the data it contains, (also known as attribute values),
  • a set of operations that can invoked on them.

In UML, objects are also rectangles, but unlike classes they typically have a single compartment and are labeled with an object name and optionally the class (e.g., john : Employee, dawin : Department).

In UML, attributes are listed in the middle compartment (e.g., class Course with attributes code : String, year : int; class City with name : String, population : int; class Employee with salary : Currency). Note the notation name : Type (Pascal-style), which UML adopts.

classDiagram
    class Course {
        code: String
        year: int
    }
    class Employee {
        salary: Currency
    }
    class City {
        name: String
        inhabitants: long
    }

We also need to define the operations on the data: these are the methods. Methods describe how we can operate on internal data; they are analogous to functions in procedural languages (with a name, parameters, and an optional return type).

In UML, methods are listed in the bottom compartment.

classDiagram
    class Employee {
        id: int
        name: String
        salary: double
        printName(): void
        getSalary(): double
    }

We can consider objects communicating by message passing. Not by direct access to object’s local data. A message is a service request that is performed by a method.

This is an abstract view that is independent from specific programming languages. It allows understanding how a message (the invocation of a method) is decoupled from the actual exceution of the operation.

Often it’s helpful to describe these message exchanges with interaction diagrams (sequence diagrams), where we show objects and the messages among them over time.

1.3.3 Associations

So far we’ve considered classes in isolation, but real programs have multiple classes related to each other and working together. In UML, links among classes are represented by associations, indicating a relationship between two classes. Usually, pairs of objects from those classes will be connected by that association. We define associations at the class level (set-level), not by enumerating every object-level link.

Associations are drawn as lines (possibly with a name and reading direction). For example, Student “attend” Course; Employee “works in” City; some associations are more generic (“residence”) and don’t need a reading direction.

classDiagram
direction LR
    Student --> Course: Attends
    Employee --> City: Works_in
    Employee --> City: Residence

Associations can be recursive (a class linked to itself), e.g., Student “friend” Student. Such an association doesn’t mean a student is a friend of themself; it means some pair of students can be linked by friendship. Some associations are symmetric (friendship), others are not (e.g., supervises between Manager and Employee).

UML lets us name the roles at each end (e.g., manager and employee).

classDiagram
direction LR
    Student --> Student: Friend
    Employee "* supervised" -- "0..1 supervisor" Employee: Supervise

A link is an occurrence (instance) of an association is a pair made up of the occurrences (instances) of the entities, one for each involved class

Another important concept is multiplicity: how many objects of one class can be linked to how many of the other? Multiplicities are shown as lower..upper bounds at each end (e.g., 0..4). Often we use 0 or 1 for the lower bound and 1 or * (“many”) for the upper bound. This matters for implementation choices (a single reference vs. a collection).

Multiplicity should be specified for each class participating in an association

  • Minimum: 0 or 1
    • 0 means the participation is optional,
    • 1 means the participation is mandatory;
  • Maximum: 1 or *
    • 1: object is involved in at most one link
    • *: each object is involved in many links
classDiagram
direction LR
    Car "0..1 " --> "0..4" Wheel: mount
Figure 1.8: Example of multiplicity in UML
  • 0..4 near class Wheel means that a car can mount none, up to four wheels.
  • 0..1 near class Car means that a whell can be mounted on at most one car, possibly none.

An example where the multiplicties are used up to their max is where you a car that is connected to four wheels, as for the object car and the wheels wheel1 .. whell4 in Figure 1.9. Another option is to have one wheel disconnected therefore (wheel5) whose multiplicity w.r.t. the link to car is 0, and the car has only three wheels mounted (multiplicity 3). Another confituration of objects that is compatible with the multiplicity defined above is that of Car3 which is connected to no wheel and the wheels wheel9 .. wheel12connected to no car.

car1 car1 wheel1 wheel1 car1--wheel1 wheel2 wheel2 car1--wheel2 wheel3 wheel3 car1--wheel3 wheel4 wheel4 car1--wheel4 car2 car2 wheel5 wheel5 wheel6 wheel6 car2--wheel6 wheel7 wheel7 car2--wheel7 wheel8 wheel8 car2--wheel8 car3 car3 wheel9 wheel9 wheel10 wheel10 wheel11 wheel11 wheel12 wheel12
Figure 1.9: Example object multiplicity

It is important to observe that the multiplicity notation of UML class diagrams is very different from what you have in ER, especially the notation in use at Politecnico di Torino.

The class diagram in Figure 1.8 can be described in UML as

Car Car Mount Mount Car--Mount 0,4 Wheel Wheel Mount--Wheel 0,1
Figure 1.10: Example ER equivalent multiplicity

A special kind of association is Aggregation/composition, indicated by a diamond. It expresses a whole-part relationship; e.g., Car is composed of Engine, Wheel(×4), CDPlayer.

classDiagram
    direction TB
    class Car
    Car o-- Engine
    Car o-- Infotainment
    Car o-- Gearshift
Figure 1.11: Example of aggregation in UML

1.3.4 Inheritance

Inheritance A class can be a sub-type of another class. The inheriting class contains all the methods and fields of the class it inherited from plus any methods and fields it defines. The inheriting class can override the definition of existing methods by providing its own implementation. The code of the inheriting class consists only of the changes and additions to the base class.

classDiagram
direction LR
A <|-- B

B specializes A means that

  • B has the same characteristics as A in terms of
    • Attributes
    • Methods
    • Participation in associations
  • B may have additional characteristics

In practice B is a special case of A, or viceversa, A is a generalization of B.

classDiagram
direction TB
class Person {
    First: String
    Last: String
    SSN: String
}
class Employee {
    Salary: double
}
class Student {
    ID: int
}
Person <|-- Employee
Person <|-- Student

Inheritance terminology

  • Class one above: Parent class
  • Class one below Child class
  • Class one or more above: Superclass, Ancestor class, Base class
  • Class one or more below: Subclass, Descendent class, Derived class

Motivation for using inheritance. Frequently, a class is merely a modification of another class. In this way, there is minimal repetition of the same code. Localization of code: fixing a bug in the base class automatically fixes it in the subclasses; adding functionality in the base class automatically adds it in the subclasses. Less chances of different (and inconsistent) implementations of the same operation Example of inheritance tree

Twitter (simplified) A registered user can Post a tweet Follow another user Reply to a tweet Add a like to a tweet

1.4 Building models

Essential guidelines:

  • If a concept has significant properties and/or describes types of objects with an autonomous existence, it can be represented by a class.

  • If a concept has a simple structure, and has no relevant properties associated with it, it is likely an attribute of a class.

  • If a concept provides a logical link between two (or more) entities, it is convenient to represent it by means of an association.

  • Any operation that implies access to the attributes of a class should be defined as a method.

  • If one or more concepts are special cases of another concept, it is convenient to represent them by means of a generalization.

  • When distinct classes may play the same role w.r.t. an association to a given class it is common to represent this commonality by generalization Inheritance includes also associations.

Modeling strategies:

  • Top-down: Start with abstract concepts and perform successive refinements
  • Bottom-up: Start with detailed concepts and proceed with integrating different pieces together
  • Inside-out: Like bottom-up but beginning with most important concepts first
  • Hybrid: a mix of the previous strategies

1.4.1 Model quality

  • Correctness No requirement is misrepresented
  • Completeness All requirements are represented
  • Readability It is easy to read and understand
  • Minimality There are no avoidable elements