400 likes | 534 Views
פרק 8 נקודות חשובות בתרגילים 7-9. 1. Initializer. Inheritance. Make the PressureSensor and TemperatureSensor inherit from the base class Sensor. Add directed associations from the Motor to the PressureSensor and to the TemperatureSensor. Set the multiplicity to 1.
E N D
פרק 8נקודות חשובות בתרגילים 7-9 תיכון תוכנה: ד"ר ראובן גלנט
1. Initializer תיכון תוכנה: ד"ר ראובן גלנט
Inheritance • Make the PressureSensor and TemperatureSensor inherit from the base class Sensor. • Add directed associations from the Motor to the PressureSensor and to the TemperatureSensor. • Set the multiplicity to 1. תיכון תוכנה: ד"ר ראובן גלנט
Base Class Sensor • Add attribute name of type OMString • Add a constructor that receives an argument aName of type OMString • Initialize the attribute in the initializer name(aName) • Create an operation print with implementation std::cout << name << “ “; תיכון תוכנה: ד"ר ראובן גלנט
Base Class Sensor-Sensor.cpp //file Sensor.cpp #include “Sensor.h” Sensor::Sensor(OMString aName): name(aName) { } void Sensor::print() {std::cout << name << “ “;} תיכון תוכנה: ד"ר ראובן גלנט
Motor Class • Create a constructor that creates instances of both types of Sensor: • setItsTemperatureSensor(new TemperatureSensor(“T1”)); • setItsPressureSensor(new PressureSensor(“P1”)); תיכון תוכנה: ד"ר ראובן גלנט
Class Motor- Motor.cpp //file Motor.cpp #include “Motor.cpp #include “Sensor.h” Motor::Motor() { setItsTemperatureSensor(new TemperatureSensor(“T1”)); setItsPressureSensor(new PressureSensor(“P1”)); } תיכון תוכנה: ד"ר ראובן גלנט
Derived Sensor Classes • For both derived Sensor classes: • Create a constructor that has an argument aName of type OMString • Set the Initializer to Sensor(aName) Sensor(aName) invokes the Sensor constructor so as to initialize the name field of the base class. תיכון תוכנה: ד"ר ראובן גלנט
Derived Classes: TemperatureSensor.cpp //file TemperatureSensor.cpp #include “TemperatureSensor.h” TemperatureSensor::TemperatureSensor(OMString aName): Sensor(aName) { } תיכון תוכנה: ד"ר ראובן גלנט
Animating • Create a Test component and a Debug configuration that creates an initial instance of the Motor class • Save / Generate / Make / Run • With the browser, note that there are two instances of Sensor. Each Sensor has a name that has been initialized. תיכון תוכנה: ד"ר ראובן גלנט
User Types • Using the browser, right-click on the Default package and select “Add New Type” • add a type tTempUnits declared as • enum tTempUnits { CELSIUS, FAHRENHEIT }; An alternative declaration is : enum %s { CELSIUS, FAHRENHEIT}. תיכון תוכנה: ד"ר ראובן גלנט
Attribute Unit • Add an attribute unit of type tTempUnits for the TemperatureSensor. • Add an argument to the TemperatureSensor constructor called aUnit of the same type • In the initializer add ,unit(aUnit) תיכון תוכנה: ד"ר ראובן גלנט
TemperatureSensor • Change the read operation to : std::cout << “Temperature = “ << rand() % 100 << “deg “; if ( unit == CELSIUS ) std::cout << “C” << std::endl; else std::cout << “F” << std::endl; • In the Motor constructor, add the argument “CELSIUS” as follows: setItsTemperatureSensor (new TemperatureSensor( “T1”, CELSIUS)); תיכון תוכנה: ד"ר ראובן גלנט
2. Container Classes (OMCollection)andIterators(OMIterator) תיכון תוכנה: ד"ר ראובן גלנט
Using OMIterator • Rhapsody provides an OMIterator class that can be used as follows to iterate through a container: • OMCollection<Sensor*> itsSensor; // a container • OMIterator<Sensor*> iSensor(itsSensor); • iSensor.reset(); // point to first • while ( *iSensor != NULL ) { • (*iSensor)->print(); // print • ++iSensor; // point to next • } תיכון תוכנה: ד"ר ראובן גלנט
Collection of Sensors • Load the “Virtual” project and save as “Collection” • Delete from Model the relations between the Motor and the Sensors. Check in the Browser that these relations have been deleted from the model not just the view. • Add a directed aggregation itsSensor from the Motor to the Sensor. Set Multiplicity to * (many). תיכון תוכנה: ד"ר ראובן גלנט
OMCollection • Delete implementation of Motor constructor • Save / Generate / Examine code for Motor • Note that the relation has been implemented as a collection of Sensors: OMCollection<Sensor*> itsSensor; • Note also that there is an operation addItsSensor(Sensor* p_Sensor); תיכון תוכנה: ד"ר ראובן גלנט
OMCollection: Motor.h #ifndef Motor_H #define Motor_H #include "PressureSensor.h" #include "TemperatureSensor.h" class Sensor; public : //defined by user void addSensor(); void deleteSensor(); void pollSensors(); public: //defined by Rhapsody OMIterator<Sensor*> getItsSensor() const; void addItsSensor(Sensor* p_Sensor); void removeItsSensor(Sensor* p_Sensor); void clearItsSensor(); protected : OMCollection<Sensor*> itsSensor; }; #endif תיכון תוכנה: ד"ר ראובן גלנט
Adding to OMCollection • In the motor constructor add Sensors: • addItsSensor(new TemperatureSensor(“Sensor1”,CELSIUS)); • addItsSensor(new TemperatureSensor(“Sensor2”,FAHRENHEIT)); • addItsSensor(new PressureSensor(“Sensor3”) ); תיכון תוכנה: ד"ר ראובן גלנט
2.a Dependencies תיכון תוכנה: ד"ר ראובן גלנט
Dependencies • In order to compile, the Motor needs to include the Pressure and Temperature Sensors header files. To do this we will add dependencies from the Motor to those classes: • Double-click on each dependency and select the stereotype Usage. תיכון תוכנה: ד"ר ראובן גלנט
Implementation Includes • Alternatively instead of drawing dependencies, we can just modify the properties for the Motor class and for CPP_CG->Class->ImpIncludes add TemperatureSensor.h,PressureSensor.h CPP_CG means C++ Code Generation. תיכון תוכנה: ד"ר ראובן גלנט
Multiple Relation • Save / Generate / Make / Run • Show that the Motor has a collection of three Sensors. תיכון תוכנה: ד"ר ראובן גלנט
Statechart • Create a simple Statechart for the Motor class that calls a pollSensors() routine every two seconds. תיכון תוכנה: ד"ר ראובן גלנט
pollSensors() • Create the pollSensors() operation that will poll all the Sensors in the collection: • OMIterator<Sensor*> iSensor(itsSensor); • for ( iSensor.reset(); *iSensor; ++iSensor ) { • (*iSensor)->print(); • (*iSensor)->read(); • } • cout << "---------------------" << endl; Note that if any more Sensors of any other type are added, the operation still functions! תיכון תוכנה: ד"ר ראובן גלנט
Extended Exercise • For the Sensor class, add a static attribute numberOfSensors of type int with initial value 0. • In the Sensor Constructor add numberOfSensors++; • For the Sensor class, add a virtual Destructor with implementation : numberOfSensors--; • Add the following to pollSensors() cout << “Number of sensors = “ << Sensor::getNumberOfSensors() << endl; • Generate code and execute to check that numberOfSensors = 3. תיכון תוכנה: ד"ר ראובן גלנט
3. Threads, Active Classes, Statechart Inheritance תיכון תוכנה: ד"ר ראובן גלנט
Concurrency • We want each Sensor to run on its own thread (active class). • To do so, we need each Sensor to be Reactive (class that waits for events). • So we will create a Statechart for the base Sensor class as follows: תיכון תוכנה: ד"ר ראובן גלנט
Active Classes • With the browser, change the concurrency of the Sensor class from sequential to active. תיכון תוכנה: ד"ר ראובן גלנט
Inheriting Behavior • Open the Statecharts for the PressureSensor and TemperatureSensor. Note that they have inherited the base class Statechart. • Specialize the behavior of the TemperatureSensor as below: Grayed out indicating inherited behavior תיכון תוכנה: ד"ר ראובן גלנט
Starting the Behavior • Add a call to startBehavior() from the TemperatureSensor and PressureSensor constructors to initialize the statecharts. If we had used a composite class, Rhapsody would have done this for us, but that would have been too easy ! תיכון תוכנה: ד"ר ראובן גלנט
Multi-threads • Save / Generate / Make / Run • Check that there are four active threads. • Setting the focus to a particular thread displays the call stack and event queue for that thread. There will always be one thread called mainThread. תיכון תוכנה: ד"ר ראובן גלנט
Suspending Threads • Note that a thread can be suspended. תיכון תוכנה: ד"ר ראובן גלנט
Problems with the Design • With the current design there are a few potential problems: • The Motor class needs to know about all the different types of Sensor. • If another class wants access to the Sensors, it too will need to depend upon all the different types of Sensor. • Starting the behavior of a Sensor, in the constructor is not very elegant. • Adding a new type of Sensor means finding and modifying all classes that use Sensor. תיכון תוכנה: ד"ר ראובן גלנט
4. Singleton, Abstract Factory תיכון תוכנה: ד"ר ראובן גלנט
Improving the Design • Using the “factory method design pattern” will solve all these concerns. • A SensorFactory class can be introduced that is used by all classes ( ex: Motor ) that need to get a Sensor. This decouples the Motor class from the actual Sensors and can also start the behavior of the Sensors. • The SensorFactory will be implemented using the “Singleton design pattern” (to ensure that there is only one instance of SensorFactory). See the “SensorFactory” example. תיכון תוכנה: ד"ר ראובן גלנט
The Improved Design תיכון תוכנה: ד"ר ראובן גלנט
The Singleton Design Pattern I Static attribute Protected constructor תיכון תוכנה: ד"ר ראובן גלנט
The Singleton Design Pattern II Static factory operation Calling the createRandomSensor operation תיכון תוכנה: ד"ר ראובן גלנט
SensorFactory::createRandomSensor() Sensor * SensorFactory::createRandomSensor(){ Sensor* aSensor; OMString aName; char index[10]; //itoa(Sensor::getCount(), index, 10); strcpy ( index, "1" ); aName = "Sensor" + OMString(index); switch ( rand() % 4 ) { default: case 0: aSensor = new TemperatureSensor( aName, CELSIUS ); break; case 1:aSensor= new TemperatureSensor( aName, FAHRENHEIT ); break; case 2: aSensor = new PressureSensor( aName ); break; case 3: aSensor = new SpeedSensor( aName ); break; } aSensor->startBehavior(); return aSensor; } תיכון תוכנה: ד"ר ראובן גלנט