Skip to content

Instantly share code, notes, and snippets.

@cardasac
Last active May 12, 2019 19:22
Show Gist options
  • Save cardasac/b69844a34090644ec17e6c054723fce7 to your computer and use it in GitHub Desktop.
Save cardasac/b69844a34090644ec17e6c054723fce7 to your computer and use it in GitHub Desktop.
SE-2019

1. Testing

a)

Test Driven Development

Definition

Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle:

  • first the developer writes an (initially failing) automated test case that defines a desired improvement or new function
  • then produces the minimum amount of code to pass that test
  • and finally refactors the new code to acceptable standards.

Kent Beck, who is credited with having developed or 'rediscovered' the technique, stated in 2003 that TDD encourages simple designs and inspires confidence.

Test-driven development is related to the test-first programming concepts of extreme programming, begun in 1999.

Cycle

  1. Add a test
  2. Run all tests to see if new one fails
  3. Write some code
  4. Run test
  5. Refactor code
  6. Repeat

Advantages

A 2005 study found that using TDD meant writing more tests and, in turn, programmers who wrote more tests tended to be more productive. Hypotheses relating to code quality and a more direct correlation between TDD and productivity were inconclusive.

Programmers using pure TDD on new projects reported they rarely felt the need to invoke a debugger. Used in conjunction with a version control system, when tests fail unexpectedly, reverting the code to the last version that passed all tests may often be more productive than debugging.

Test-driven development offers more than just simple validation of correctness, but can also drive the design of a program. By focusing on the test cases first, one must imagine how the functionality is used by clients. So, the programmer is concerned with the interface before the implementation. This benefit is complementary to Design by Contract as it approaches code through test cases.

Test-driven development offers the ability to take small steps when required. It allows a programmer to focus on the task at hand as the first goal is to make the test pass. Exceptional cases and error handling are not considered initially, and tests to create these extraneous circumstances are implemented separately. Test-driven development ensures in this way that all written code is covered by at least one test. This gives the programming team, and subsequent users, a greater level of confidence in the code.

While it is true that more code is required with TDD than without TDD because of the unit test code, the total code implementation time could be shorter. Large numbers of tests help to limit the number of defects in the code. The early and frequent nature of the testing helps to catch defects early in the development cycle, preventing them from becoming endemic and expensive problems. Eliminating defects early in the process usually avoids lengthy and tedious debugging later in the project.

TDD can lead to more modular, flexible, and extensible code. This effect often comes about because the methodology requires that the developers think of the software in terms of small units that can be written and tested independently and integrated together later. This leads to smaller, more cohesive classes, looser coupling, and cleaner interfaces.

Because no more code is written than necessary to pass a failing test case, automated tests tend to cover every code path. For example, for a TDD developer to add an else branch to an existing if statement, the developer would first have to write a failing test case that motivates the branch. As a result, the automated tests resulting from TDD tend to be very thorough: they detect any unexpected changes in the code's behavior. This detects problems that can arise where a change later in the development cycle unexpectedly alters other functionality.


Independent Testing

  • Programmers have a hard time believing they made a mistake plus a vested interest in not finding mistakes
  • Design and programming are constructive tasks, testers must seek to break the software
  • Wrong conclusions:
    • The developer should not be testing at all, recall “test before you code”
    • Testers only get involved once software is done
    • Toss the software over the wall for testing
    • Testers and developers collaborate in developing the test suite
    • Testing team is responsible for assuring quality, quality is assured by a good software process

Unit Testing

Focus on smallest unit of design

  • A procedure, a class, a component
  • Test the following
    • Local data structures
    • Basic algorithm
    • Boundary conditions
    • Error handling
  • May need drivers and stubs
  • Good idea to plan unit tests ahead

Integration Testing

  • If all parts work, how come the whole doesn’t?
  • For software, the whole is more than the sum of the parts
    • Individual imprecision is magnified
    • Unclear interface design
  • Don’t try the “big bang” integration !
  • Do incremental integration
    • Top-down integration
    • Bottom-up integration

Top-Down Integration

  • Test the main control module first
  • Slowly replace stubs with real code
    • Can go depth-first along a favorite path, to create a working system quickly
    • Or breadth-first
  • Problem: you may need complex stubs to test higher-levels

Bottom-Up Integration

  • Integrate already tested modules
  • No stubs, but need drivers
    • Often the drivers are easier to write
  • Example:
    • Financial code that depends on subroutine for computing roots of polynomials
    • We cannot test the code without the subroutine, a simple stub might not be enough
    • We can develop and test the subroutine first
  • Plan for testability !

VS

Check this out

Validation Testing

  • Culmination of integration testing and functional testing
    • the software works, but does it do what we need?
  • Run acceptance tests
    • get your customer to define them
  • Alpha-testing (in controlled environment)
    • With developer looking over the shoulder
  • Beta-testing
    • At end-user sites

Regression Testing

Idea

  • When you find a bug,

  • Write a test that exhibits the bug

  • And always run that test when the code changes

  • So that the bug doesn’t reappear

  • Without regression testing, it is surprising how often old bugs reoccur.

  • Regression testing ensures forward progress, never go back to old bugs.

  • Regression testing can be manual or automatic, ideally run regressions after every change to detect problems as quickly as possible.

  • But, regression testing is expensive and it limits how often it can be run in practice, reducing cost is a long standing research problem.

  • Other tests (besides bug tests) can be checked for regression:

    • requirements/acceptance tests
    • performance tests
  • Ideally, entire suite of tests is rerun on a regular basis to assure old tests still work.


Calculator

public class Calculator 
{
	public int evaluate(String expression) 
	{
		int sum = 0;
		for (String summand: expression.split("\\+"))
		{
		      sum += Integer.valueOf(summand);
		}
		return sum;
	}
}

Test Calculator

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class CalculatorTest 
{
	@Test
	public void evaluatesExpression()
	{
		Calculator calculator = new Calculator();
		int sum = calculator.evaluate("1 + 2 + 3");
		assertEquals(6, sum);
	}
}

2. Restaurant domain analysis

a)

Display Booking realization

USE case realisation In the application domain, the bookings made can be viewed as an attribute of the restaurant itself. One possible strategy, therefore, would be to introduce a new class to represent the restaurant and give this class the responsibility of maintaining links to all the bookings made, and of locating and returning particular bookings when requested. With this assumption, the use case realization for displaying bookings can be refined to the next level.

This diagram shows what the system does in response to the message from the staff member. The resulting messages are shown originating from the activation on the booking system’s lifeline that corresponds to the system message, thus showing the nested flow of control that is characteristic of procedural message calls. The return message is explicitly annotated to show what data is returned.

Following this, a message is sent to update the current display. In terms of the system architecture, this message should be sent from the booking system to some class in the presentation layer, requesting that the display be updated. The ‘updateDisplay’ message in Figure above corresponds to a method that communicates this to the presentation layer; a mechanism by which this can be accomplished is described in the next chapter. It is likely that this method will be called on many different occasions, whenever the information to be displayed has changed.

Cancel booking

The use case for cancelling a booking has a more complicated structure than the ones realized so far, as the user’s participation in the use case consists of more than one interaction with the system. As described in Chapter 4, to cancel a booking the user must first select the booking to be cancelled, then cancel it and finally confirm the cancellation when prompted by the system to do so. A sequence diagram showing a possible realization of this course of events is shown in Figure 5.9.

This use case breaks down into two separate parts. First, the required booking is selected. There are many ways in which this could be done: perhaps the user clicks on the booking rectangle on the screen or enters some data which identifies the booking. This is realized in Figure 5.9 by a message ‘selectBooking’ from the user. At the analysis level, the exact nature of the information supplied by the user to identify the booking is left unspecified and the diagram simply indicates informally that sufficient is supplied to identify the required booking.

Cancel Booking In order to locate the required booking object, each currently displayed booking must be examined. As the booking system object already has the responsibility for maintaining this set of bookings, it is in a position to iterate directly through the bookings. The user interface checks the details of all the current bookings to see which matches the selection criterion. This is indicated by the ‘getDetails’ message in Figure 5.9. The multiplicity before the message indicates that it will be sent zero or more times, depending on how many currently displayed bookings there are.

In the basic course of events, we are assuming that a booking is successfully selected. As this booking is involved in the later stages of the use case, it is shown separately at the top of the diagram. Role names are used to distinguish the selected booking from the current bookings. A role name does not name an individual object, but describes the role that an object can play in the interaction. Each time this use case is performed, a different object may be the selected booking.

Once the required booking has been selected, and presumably highlighted on the screen in some way, the user invokes the cancel operation. The process of getting confirmation from the user is modelled by a message going from the boundary object back to the user: this may correspond to the display of a confirmation dialogue box, for example. The user is now constrained to respond to this message in some way, and the return message indicates the user’s response.

Once the user’s response is received, the user interface object deletes the selected booking and updates the display before the use case ends. Object deletion is indicated by a message with the ‘destroy’ stereotype: when an object receives such a message, its lifeline is terminated with a large ‘X’, indicating the destruction of the object. Notice that the form that object destruction takes may vary among programming languages and that, in languages with automatic garbage collection, no explicit method call may be required in order to delete an object.

b)

Refine domain model

A new responsibility is implicit in this interaction, namely the responsibility for remembering which is the selected booking. If this is not recorded somewhere, the booking system object will not know which booking to destroy. The sequence diagram in Figure 5.9 assumes that this responsibility is given to the booking system object: as that object is already responsible for maintaining the set of currently displayed bookings, this coheres well with its existing responsibilities. This responsibility needs to be reflected in the evolving class diagram by means of an additional association between the booking system and booking classes, as shown in Figure 5.10. Notice the different multiplicities for these associations: there can be many bookings currently displayed, but at most one of these can be selected. There is an implicit constraint related to these associations, namely that the selected booking must be one of the currently displayed bookings.

Class model

Diagram check bellow for Analysis Model.

Analysis Model

Notation for analysis class stereotypes

Diagram Here

c) Observer pattern

Definition

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

It is mainly used to implement distributed event handling systems, in "event driven" software. Most modern languages such as C# have built-in "event" constructs which implement the observer pattern components.

The observer pattern is also a key part in the familiar model–view–controller (MVC) architectural pattern. The observer pattern is implemented in numerous programming libraries and systems, including almost all GUI toolkit.

What problems it solves

The Observer pattern addresses the following problems:

  • A one-to-many dependency between objects should be defined without making the objects tightly coupled.
  • It should be ensured that when one object changes state an open-ended number of dependent objects are updated automatically.
  • It should be possible that one object can notify an open-ended number of other objects.

Defining a one-to-many dependency between objects by defining one object (subject) that updates the state of dependent objects directly is inflexible because it couples the subject to particular dependent objects. Tightly coupled objects are hard to implement, change, test, and reuse because they refer to and know about (how to update) many different objects with different interfaces.

The solution it offers

  • Define Subject and Observer objects.
  • so that when a subject changes state, all registered observers are notified and updated automatically.

The sole responsibility of a subject is to maintain a list of observers and to notify them of state changes by calling their update() operation.
The responsibility of observers is to register (and unregister) themselves on a subject (to get notified of state changes) and to update their state (synchronize their state with subject's state) when they are notified.
This makes subject and observers loosely coupled. Subject and observers have no explicit knowledge of each other. Observers can be added and removed independently at run-time.
This notification-registration interaction is also known as publish-subscribe.

See also the UML class and sequence diagram below.

Class Diagram and Sequence Diagram

Link to Diagram

In the above UML class diagram, the Subject class doesn't update the state of dependent objects directly. Instead, Subject refers to the Observer interface (update()) for updating state, which makes the Subject independent of how the state of dependent objects is updated. The Observer1 and Observer2 classes implement the Observer interface by synchronizing their state with subject's state.

The UML sequence diagram shows the run-time interactions: The Observer1 and Observer2 objects call attach(this) on Subject1 to register themselves. Assuming that the state of Subject1 changes, Subject1 calls notify() on itself.
notify() calls update() on the registered Observer1 and Observer2 objects, which request the changed data (getState()) from Subject1 to update (synchronize) their state.

Applying Design pattern

This problem is typical of the situations faced by designers, and a lot of work has been put into documenting solutions to common design problems as design patterns. In this case, a suitable solution is provided by the Observer pattern defined in the influential book by Gamma et al. (1995). The definition of this pattern states that it is applicable:

  • ‘When a change to one object requires changing others, and you don’t know how many objects need to be changed.
  • When an object should be able to notify other objects without making assumptions about who those objects are.’

The present situation contains elements of both these conditions: we want a change in an application object to be followed by a change in the view object, and we want the application object to be able to notify the view object of this change without being dependent on the view object. The definition of the pattern is rather abstract and in this section only its application will be described.

The application of the Observer pattern to allow display updating in the restaurant booking system is shown in Figure 6.4 and the packages representing the different layers in the application are also shown. Notice that the relationships between classes that cross the package boundaries are consistent with the direction of the dependency between the two layers.

Observer

d) Use case realization

The simplest case in the booking system is probably the ‘Display bookings’ case. It is also central to the system, in that it is included by several other use cases and is responsible for updating the display that the user sees. It is therefore a sensible place to start the realization of the booking system’s use cases. The interaction specified in the basic course of events is very simple: the user enters the required date and the system responds by displaying all the bookings recorded for this date.

3. Restaurant design analysis

a) Layered Architecture

The idea of defining the architecture of a system in a number of layers is an old one in software engineering and has been applied in many different domains. The motivation for defining layers is to allocate responsibilities to different subsystems and to define clean and simple interfaces between layers. In many ways, this can be viewed as an application of the same principles as in object design, but applied to larger components within the system.

In the architecture that will be described here, one of the fundamental ideas is to make a clear distinction between those parts of the system responsible for maintaining data and those responsible for presenting it to the user. Perhaps the earliest formulation of these ideas was the so-called ‘Model–View–Controller’, or MVC, architecture defined for programs written in the Smalltalk language.

It is often tempting when writing an object-oriented program to create a single class that both holds the data corresponding to some entity and also displays it on the screen. This approach appears to make it easy, for example, to update the display when the data changes: as the same object is responsible for both tasks, when its state changes it can simply update its display to reflect the new state.

However, this approach does not work well when the display consists of a complex presentation of many different objects. In order to display its state sensibly, an object may have to be aware of everything else on the screen, thus significantly increasing the complexity of the class. Further, if the user interface changes, all classes in the system would have to be changed if the responsibility for displaying data is distributed widely. To cope with these problems, the MVC architecture recommends that these responsibilities should be given to different classes, with a model class being responsible for maintaining the data and a view class for displaying it.

Designing a system in accordance with this pattern will have the effect of increasing the number of messages that are passed between objects while the system is running. Whenever a display has to be updated, for example, the view class will have to obtain the latest state of the object from the model class before displaying it.

Nevertheless, the benefits of this approach have been found to make this worthwhile. Among other things, it becomes very easy to define multiple or alternative views of the same data. By using different views, the same application could, for example, support user interfaces based on desktop PCs and on mobile phones. If the maintenance and display of data were defined in the same class, it would be much harder to separate the two and safely update the user interface code.

This principle of making a distinction between the model and the view can be applied at a system-wide level, leading to the identification of two separate layers in the architecture. Classes that maintain the system state and implement the business logic of the application are placed in an application layer, whereas classes concerned with the user interface are placed within a presentation layer.

These two layers are illustrated graphically in Figure 5.1. Each layer is represented by a package, shown as a ‘tabbed folder’ icon containing the package name. Packages in UML are simply groupings of other model elements and are used to define a hierarchical structure on models. A model can be divided into a number of packages, each of which can contain further nested packages, to any level deemed necessary or helpful. The contents of a package can, but need not, be shown inside the icon. If they are not, the package name is written inside the folder icon.

Basic

Figure 5.1 also shows a dependency between the presentation and application layers, stating that the presentation layer depends on, or makes use of, the classes and other model elements defined in the application layer, but not vice versa. This is a typical feature of layered architectures, where ‘upper’ layers can make use of the features provided by ‘lower’ layers, but the lower layers are defined independently of any upper layers.

Applied Example of layers

Restaurant

b) Persistent storage

The overwhelming majority of software systems need some way of storing persistent data. This term refers to data that is stored permanently in some way, so that it is not lost when the system is closed down but can be reloaded when required. In the case of the restaurant booking system, it is obviously a system requirement that booking data, for example, should be saved and reloaded on subsequent days when the system is restarted.

There are a variety of possible storage strategies that can be used to provide persistency. A simple approach is simply to write data to disc files, but in the majority of cases a database management system will be used. Although some object-oriented databases are available, the commonest database technology in use is still based on the relational model. In this section, therefore, the design will be based on the assumption that a relational database will be used to provide persistent storage.

Mixing an object-oriented program with a relational database is not a straight-forward task, however, as the two technologies approach data modelling in somewhat incompatible ways. In this section, a simple approach to the problem will be outlined, but a systematic treatment of the issue is beyond the scope of this book. This section assumes a basic knowledge of relational databases and the associated terminology.

The implementation breaks down into two distinct areas. First, it is necessary to design a database schema that will allow the data held in the system’s objects to be stored and retrieved. Second, the code to access the database and to read data from and write data to it must be designed.

The first step in implementing persistent data storage for the booking system is to decide what data needs to be persistent. It is clear that bookings will need to be saved across sessions, as the whole point of the system is to capture and record booking information. Along with this, the table and customer objects that bookings are linked to will also need to be persistent.

By contrast, the data stored in the booking system object, namely the date of the displayed bookings and the set of current bookings, does not need to be stored. When the system is started up, it will usually be of no interest to the user what was displayed last time the system was in use and, if bookings in general are persistent, no irreplaceable data will be lost by not storing the set of current bookings. The remaining class in the application layer, the ‘Restaurant’ class, does not really have any properties of its own, but acts as an interface to the system’s data; provided this data is stored, it does not need to be made persistent.

Designing a database schema

The first step in implementing persistent data storage for the booking system is to decide what data needs to be persistent. It is clear that bookings will need to be saved across sessions, as the whole point of the system is to capture and record booking information.

Along with this, the table and customer objects that bookings are linked to will also need to be persistent. By contrast, the data stored in the booking system object, namely the date of the displayed bookings and the set of current bookings, does not need to be stored. When the system is started up, it will usually be of no interest to the user what was displayed last time the system was in use and, if bookings in general are persistent, no irreplaceable data will be lost by not storing the set of current bookings. The remaining class in the application layer, the ‘Restaurant’ class, does not really have any properties of its own, but acts as an interface to the system’s data; provided this data is stored, it does not need to be made persistent.

These decisions can be recorded on the design class diagram using a tagged value in the appropriate classes. Tagged values are a way of recording arbitrary properties of model elements, in the form of name–value pairs. The tagged value ‘persistence’ is used to indicate whether a class is persistent or not. It has two values, the default ‘transitory’ for classes that are not persistent and ‘persistent’ for those that are. A tagged value is recorded in the form ‘persistence = persistent’ but in Figure 6.6, which shows the persistent classes in the restaurant booking system, the tagged value is shown in a widely-used abbreviated form.

Persistent

The only bookings that ever get created in the booking system are walk-ins and advance reservations, and these are stored as instances of the respective subclasses of the ‘Booking’ class. This means that ‘Booking’ is an abstract class, as shown on the diagram. It may seem a little strange to mark an abstract class as persistent, but some of the data it contains is inherited by the subclasses and so is permanently stored.

The ‘persistence’ tagged value can be applied to model elements other than classes, and in particular associations can be marked as being persistent. Often, however, this is left implicit on diagrams, the assumption being made that any association between persistent classes is itself persistent. In the restaurant booking system, then, the associations between bookings and tables and between reservations and customers will both be treated as being persistent.

4.

a) Methodologies

Agile

Overview

  • Traditional IS development Methodologies "are treated primarily as a necessary fiction to present an image of control or to provide a symbolic status".
  • Lightweight alternative to traditional heavyweight process.
  • Began with introduction of extreme programming (XP) by Beck in 1999.
  • Agile Manifesto in 2001.
  • A number of other methods have been invented or rediscovered since.
  • However, no clear agreement on how to distinguish Agile from Traditional Methodologies.

Values

  • Individuals and interactions over tools and processes.
  • Working software over comprehensive documentation – continuously turn out working tested software.
  • Customer collaboration over contract negotiation.
  • Responding to change rather than following a plan – adaptive rather than prescriptive.

Characteristics & Principles

  • Satisfy the customer through early and continuous delivery of valuable software.
  • Incremental and time bound with short iteration cycles from 1 to 6 weeks.
  • Development group consists of both software developers and customer representatives. Regular feedback to developers. Authorized to consider adjustments during development.
  • Working software is the primary measure of progress
  • What is new is the recognition of people as the primary drivers of project success along with intense focus and maneuverability.
  • Fully automated regression testing.
  • Experienced developers – speeds up project by a factor anywhere from 2 to 10 times.
  • Minimal process overheads, remove unnecessary process activities.
  • Modelling tools not as useful as generally thought.
  • Continuous attention to technical excellence, e.g. refactoring in XP.
  • At regular intervals, the team reflects on how to become more effective, then tunes and adjusts its behavior accordingly.

Agile Methodologies

  • Extreme Programming - XP
  • Scrum
  • Crystal
  • Feature Driven Development
  • UP?

XP

Exploration Phase

  • Customers write story cards for first release.
  • Each story card describes feature of system (like a brief use-case).
  • Project team familiarise themselves with tools, technology and practices they use in the project.
  • Try out tools and explore possibilities for architecture via prototyping.
  • Phase can take from a few weeks to a few months.

Planning Phase

  • Set priority for stories.
  • Agree contents of first small release.
  • Estimate effort for each story and agree schedule.
  • First release normally not more than 2 months.
  • Planning phase itself takes a couple of days.

Iterations to Release Phase

  • Several iterations from 1 to 4 weeks.
  • First iteration creates a system with architecture for whole system.
  • Select story that will enforce building of structure for whole system.
  • Customer selects stories for each iteration.
  • Functional tests created by customer run at end of each iteration.

Production Phase

  • Requires extra testing and performance checking before release to customer.
  • New changes may still be found.
  • Shorter iterations.

Maintanence and Death Phase

  • Keep system in production while producing new increments.
  • Requires effort for customer support tasks.
  • Development speed may slow.
  • Maybe incorporate new people into team.
  • Death phase – no more stories to be implemented, or if system not delivering desired outcomes or is proving too expensive for more development.

Roles

  • Programmer
  • Customer – writes stories and functional tests
  • Tester – helps customer write functional tests
  • Tracker – gives feedback on schedule to team
  • Coach – has sound understanding of XP
  • Consultant – specific technical knowledge
  • Manager

Practices

  • Planning game
  • Small short releases
  • Simple design – simplest possible design that is implementable at the moment.
  • Testing – development is test driven. Unit tests are implemented before code and run continuously. Customers write functional tests.
  • Refactoring
  • Pair programming
  • Collective ownership – anyone can change any part anytime
  • Continuous integration – new piece of code integrated into code-base as soon as it is ready. System integrated and built many times daily. All tests run and have to be passed for change to be accepted.
  • 40-hour week
  • On site customer
  • Open workspace

Scope of use

  • Small to medium sized teams, 3 to 20 project members.
  • Communication between members at all times, even scattering across two floors is not acceptable.
  • Any resistance by team members, management or customers may fail the process.
  • Growing research on XP

Scrum

Great Site

  • Developed for managing the systems development process.
  • No specific software development technique, can be adopted to manage an organizations own engineering practices.
  • Focuses on flexibility of team members in producing software in a constantly changing environment.
  • Frequent management activities to identify and rectify any impediments in the development process.
  • Suitable for small teams of less than 10
  • Development relies on several environmental and technical variables which are likely to change:
  • Requirements
    • Time frame
    • Resources
    • Technology
    • Development Methods

3 Phases

Pregame phase, which consists of 2 sub-phases - Planning - Architecture and high level design

  • Development or Game phase
  • Postgame phase

Pregame Planning sub-phase

  • Product Backlog List
    • All requirements that are currently known
    • Prioritized
    • Development effort estimated
  • Includes definition of team, tools and other resources.
  • Risk assessment
  • Training needs

Pregame Architecture sub-phase

  • High level design of system including architecture planned based on current Product Backlog.
  • For enhancement to an existing system, changes needed for implementing backlog are identified along with any problems they may cause.
  • Design review meeting held to go over proposals and decisions are made on the basis of this review.

Development or Game Phase

  • Agile part of Scrum, unpredictable expected.
  • System developed in Sprints
    • Iterative cycles where functionality is developed or enhanced to produce new increments.
    • Included traditional development activities of analysis, design, implementation, delivery
    • A Sprint is planned to last from 1 week to 1 month
    • Could be 3 to 8 sprints in a system development process before ready for distribution.
  • Environment and technical variables are observed and controlled during sprints, not just at beginning.

Postgame Phase

  • Entered when agreement reached that environmental variables such as requirements are completed
  • System ready for release
  • Integration
  • System Testing
  • Documentation

Roles & Responsibilities

  • Scrum Master
  • Product Owner
  • Scrum Team
  • Customer
  • User
  • Management
Product Owner
  • Responsible for ensuring that project is run according to practices, values and rules of Scrum.
  • Interacts with team, customer and management.
  • Responds to impediments
Product Owner
  • Responsible for managing and controlling backlog list
  • Selected by Scrum Master, Customer and Management
  • Makes final decisions on backlog, decides on features
Scrum Team
  • Self organizing in order to achieve goals
Customer
  • Participates in creating product Backlog
Management
  • In charge of final decision making
  • Also involved in selecting Product Owner
  • Gauging progress with Scrum Master

b) DbC

Introduction

Design by Contract (DbC) or Programming by Contract is an approach to designing software. It says that designers should define precise and verifiable interface specifications for software components, with the use of preconditions, postconditions and invariant. These specifications are referred to as "contracts"; in the same way as a business contract entails certain obligations and conditions.

  • developed by Bertrand Meyer, “Bubbles (aka UML boxes) don’t crash”, and forms a central feature of Eiffel
  • uses assertions – assertion is a Boolean statement that should never be false. If so, it indicates a bug
  • assertions usually only checked during debugging/testing
  • three kinds of assertions:
    • postconditions
    • preconditions
    • invariant
  • preconditions and post- conditions are associated with operations

Precondition

  • expresses something about the state of a program that should be true before an operation is executed
  • e.g. a precondition for an integer-square-root operation might be input >= 0 indicates it is an error to invoke square-root on a negative number
  • e.g. pop() should net be called on an empty stack, precondition is s.isEmpty() == false
  • makes explicit that the calling routine is responsible for ensuring that something is true before operation is invoked
  • lack of one may lead to too little or too much checking (duplicate checking code and thus more complicated program)

Postcondition

  • a statement of what things should be like after the execution of an operation
  • e.g. integer-square-root operation result * result <= input < (result+1)*(result+1)
  • expresses what an operation does rather than how it does it
  • separates implementation from interface
  • leads to a stronger definition of an exception.

Invariant

  • an assertion about a class or a method
  • invariant is always true for class instances – meaning whenever object is available for an operation to be invoked on it
  • may be temporarily false during execution of method
  • e.g. Account class balance == sum of transaction amounts
  • e.g. in the above Spec# example, r * r <= x is an invariant of the while loop
  • invariant is added to preconditions and postconditions of public methods

Precision versus Detail

DbC constraint writing in OCL is a form of coding. It is more abstract than 3GL programming as it omits many implementation details that are necessary in a 3GL. But it is still no less precise than 3GL.

Example of an electric motor to illustrate precision versus detail. Specs can be very precise while giving no detail about the motor's internals. Abstraction is suppression of irrelevant detail, not vagueness.

Can infer exceptions for operations from DbC constraints. Pre-condition maps directly to an exception to be thrown when the operation is invoked and the precondition is not satisfied.

A lot of tool support now available for checking constraints at runtime, e.g. with Spec#. Generate code for checking constraints. Can specify when in time they are to be checked.

Quality Assurance

DbC can provide a framework for quality assurance (QA). Preconditions, postconditions and invariant represent much of what QA engineer must validate. DbC constraints can be used by code generators to produce test harnesses or for program verification as in Boogie tool with Spec#.

Even when not used automatically, DbC constraints act as precise guides to QA engineers regarding what they must test. Also useful for code reviews or walk-through.

There are other non-functional factors such as scalability and performance, that are not covered by DbC assertions. Can use other UML extension for these.

Client

  • Benefit
    • no need to check output values
    • result guaranteed to comply to postcondition
  • Obligation
    • satisfy precondition

Provider

  • Benefit
    • no need to check input values
    • input guaranteed to comply to precondition
  • Obligation
    • satisfy postconditions

Exceptions

Exception occurs when an operation is invoked with its precondition satisfied and is unable to return with its postcondition true.

Subclassing/Polymorphism

  • assertions useful with polymorphism, assertions can help keep subclass operations consistent with those of superclass

  • invariants and post-conditions must be true for all subclasses, subclass can strengthen these i.e. make them more restrictive

  • not allowed to strengthen preconditions – because of substitutability, can only weaken a precondition

  • if a subclass strengthened a pre-condition, then a superclass operation could fail when applied to instance of subclass

  • preconditions: best pay-off for least overhead

  • one language to support assertions is Eiffel

In class we have already looked at the role of design by contract (DbC) in software specification. We examined the notions of weakening the pre-condition and strengthening the post-condition in cases of polymorphism and inheritance.

c) ARProcessor

-- An invoice cannot be both unprocessed and processed.

context ReceivablesAccount inv:
	unprocessedInvoices -> intersection(processedInvoices) -> isEmpty()

-- An invoice number must be six characters in length

context ReceivablesAccount inv:
	self.numbers -> size() = 6
-- ARProcessor::ProcessInvoices preconditions

-- There must be some unprocessedInvoices.

context APRocessor::ProcessInvoices(arAccounts : Set(ReceivablesAccount))
	pre: arAccounts -> forAll(unprocessedInvoices -> notEmpty())
-- ARProcessor::ProcessInvoices postconditions

-- unprocessedInvoices become processedInvoices.

context ARProcessor::ProcessInvoices(arAccounts : Set(ReceivablesAccount))
	post: arAccounts -> forAll(
		unProcessedInvoices -> isEmpty() and
		processedInvoices -> includes(unprocessedInvoices@pre))

d) Spec#

class Fig1
{
	int ISqrt(int x)
	requires 0 <= x;
	ensures result * result <= x && x < (result + 1) * (result + 1);
	{
		int r = 0;
		while ((r + 1) * (r + 1) <= x)
		invariant r * r <= x;
		{
			r++;
		}
		return r;
	}
}

5. Design Patterns

Observer in Q2

Singleton

In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one "single" instance. This is useful when exactly one object is needed to coordinate actions across the system. The term comes from the mathematical concept of a singleton.

Critics consider the singleton to be an anti-pattern in that it is frequently used in scenarios where it is not beneficial, introduces unnecessary restrictions in situations where a sole instance of a class is not actually required, and introduces global state into an application.

Overview

The singleton design pattern is one of the twenty-three well-known "Gang of Four" design patterns that describe how to solve recurring design problems to design flexible and reusable object-oriented software, that is, objects that are easier to implement, change, test, and reuse.

The singleton design pattern solves problems like:

  • How can it be ensured that a class has only one instance?
  • How can the sole instance of a class be accessed easily?
  • How can a class control its instantiation?
  • How can the number of instances of a class be restricted?

The singleton design pattern describes how to solve such problems:

  • Hide the constructor of the class.
  • Define a public static operation (getInstance()) that returns the sole instance of the class.

The key idea in this pattern is to make the class itself responsible for controlling its instantiation (that it is instantiated only once).
The hidden constructor (declared private) ensures that the class can never be instantiated from outside the class.
The public static operation can be accessed easily by using the class name and operation name (Singleton.getInstance()).

Common Uses

  • The abstract factory, builder, and prototype patterns can use singletons in their implementation.
  • Facade objects are often singletons because only one facade object is required.
  • State objects are often singletons.
  • Singletons are often preferred to global variables because:
    • They do not pollute the global namespace (or, in languages with nested namespaces, their containing namespace) with unnecessary variables.
    • They permit lazy allocation and initialization, whereas global variables in many languages will always consume resources.

Implementation

An implementation of the singleton pattern must:

  • ensure that only one instance of the singleton class ever exists; and
  • provide global access to that instance.

Typically, this is done by:

The instance is usually stored as a private static variable; the instance is created when the variable is initialized, at some point before the static method is first called. The following is a sample implementation written in Java.

private static CustomerMapper uniqueInstance;

public static CustomerMapper getInstance() 
{
        if (uniqueInstance == null) 
        {
                uniqueInstance = new CustomerMapper();
	}
	return uniqueInstance;
}

Restaurant Singleton

public class BookingSystem
{
	private static BookingSystem uniqueInstance;
	
	public static BookingSystem getInstance()
	{	
		if (uniqueInstance == null)
		{
			uniqueInstance = new BookingSystem();
		}
		return uniqueInstance;
	}
	
	private BookingSystem() { ... }
}

Classes that should only be instantiated once are called singleton classes. It is possible to implement classes in such a way that only one instance of the class is ever created, and a standard method for doing this is documented in the design pattern known as Singleton. If a class is implemented as a Singleton, at most one instance of it will be created, and that instance can be made available to other classes as required.

Singleton

The central idea behind the Singleton pattern is to make the constructor of the class inaccessible, so that classes that use the singleton class cannot create instances of the class. The singleton class stores a single instance as a static data field, and this is made available to clients through a static method that will initialize the unique instance the first time it is called. The application of this pattern to the booking system class is illustrated in the outline code below.

Adapter

Check 2017

OCL/USE(less)/SOIL

USE for Booking and Reservation

class Reservation < Booking
	-- overrides booking
	attributes
		arrivalTime : TimeDate

	operations
		setArrivalTime(td : TimeDate)
		begin
			self.td := td
		end
end
class Booking
	attributes
		covers : Integer
		td : TimeDate
		table : Table

	operations
		getTimeDate(td : TimeDate)
		getDetails()
		getCovers(c : Integer)
		setArrivalTime(td : TimeDate)
		setTable(t : Table)
end

USE

association isAtTable between
	Booking[*] 
	Table[1] 
end

assignTable(table : Table)
	begin
		insert(self, table) into isAtTable
	end

assignTable() question

Soil

!new Booking('B')
!B.covers := 5
!new Table('T1')
!openter B assignTable(T1)
!insert (T1, B) into Reserves
!opexit

Precondition

context Booking::assignTable(t : Table)
	pre preTable1 : table.isDefined()
	pre preTable0 : t.places >= self.covers

More on getTime()

context BookingSystem::makeReservation(r : Reservation, c : Customer, t : Table)
	pre preCond1: r.isDefined()
	pre preCond2: c.isDefined()
	pre preCond3: t.isDefined()
	pre preCond4: booking -> excludes(r)
	pre preCond5: t.booking.getTime() -> excludes(t.bookings.getTime() + 2)
	post postCond1: booking -> includes(r)
-- Class Reservation < Booking (generalisation)
class Reservation < Booking
	attributes

	operations
		setCustomer(c : Customer) 
		begin
			self.customer := c
		end
end
-- Create class Booking --
class Booking 
	attributes
		covers : Integer
		date: Date
		time : Time
	operations

		getTime() : Time
		begin
			result := self.time
		end

		setTime(t : Time)
		begin 
			self.time := t
		end		
end

SOIL

!new Rservation('res1')
!res1.setCustomer('Icaurs')

!new Booking('booking1')
!booking1.covers := 4
!booking1.Time := 120

!booking1.getTime()

Check previous years

No composition/inheritance

No emergent design

@cardasac
Copy link
Author

cardasac commented May 11, 2019

Restaurant Observer

image

@cardasac
Copy link
Author

Persistent Storage

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment