580 likes | 601 Views
Learn about architectural, component-level, and interface design in software engineering, key principles, and design patterns. Improve software quality and performance.
E N D
Design ECE 417/617:Elements of Software Engineering Stan Birchfield Clemson University
From modeling to design Steps: • Analysis and modeling • Design • Construction (code generation and testing) Design involves making the analysis model more specific, to facilitate construction. Goal of design is quality.
FURPS Quality software is • Functional – capabilities of program • Usable – human factors, aesthetics • Reliable – frequency and severity of failure • Performance – response time, speed • Supportable – is the code maintainable, extensible, testable, configurable, easy to install, etc. (Developed by Hewlett-Packard in 1980s)
Basic design principles • Design is iterative: • Architecture is refined over time by successively filling in details • Refinement is a process of elaboration • Results in hierarchical model • A good design exhibits • abstraction – details are provided in lower levels • modularity – divide-and-conquer via smaller independent components • refactoring – internal structure of software is improved without affecting external behavior
Design model Design model has three aspects: • Architectural design • Component-level (data) design • Interface design We will consider these in turn, with UI covered in a separate lecture.
Architectural design • Architecture is a high-level representation of the S/W with the major components identified • Architectural styles are templates, e.g., • Data-centered • Data-flow • Call and return • Object-oriented • Layered • Architectural patterns define specific approach for handling some behavioral characteristic of system, e.g., • concurrency: use O/S features or provide task scheduler • persistence: storage and retrieval of data • distribution: communication of components with one another. Most common is broker – acts as middle man between client and server (CORBA). Style is like “Cape cod, A-frame”. Pattern is like “kitchen”.
Architectural Styles • Data-centered – subsystems interact through single repository • Model / View / Controller • Call and return (Client / Server) • Layered (three-tier, four-tier) • Data-flow (pipe and filter)
Layers and Partitions • Layer – group of related subsystems • Layer knows about layers below it, but not layers above it • Top layer: no one else knows about it • Closed architecture – can only access layer immediately below • Open architecture – can access any layer below • Partition – peer subsystems, each with different responsibilities
Application Object Presentation Format CORBA Session Connection Transport Socket Message TCP/IP Network Packet DataLink Physical Ethernet Wire Frame Bit Hierarchical decomposition Level of abstraction Example: Open Systems Interconnection (OSI)
Mapping DFD into architecture • Transform mapping • transform flow always exists; represents information flow within system; incoming flow passes through transform center, leads to outgoing flow • To map DFD with transform flow characteristics into specific architectural style, • review model • refine models • determine whether transform or transaction characteristics • isolate transform center • perform first and second level factoring • refine • Transaction mapping • transaction flow occurs when one input gives rise to several outputs; transaction triggers data flow along one of many paths • To map DFD with transaction flow characteristics, • review model • refine models • determine whether transform or transaction characteristics • isolate transaction center • map to transform branch • factor and refine
Model / View / Controller (MVC) • MVC: • Model subsystems maintain domain knowledge • View subsystems display it to the user • Controller subsystems manage sequence of interactions with user • M doesn’t depend upon V or C • Changes propagated via subscribe/notify protocol, using Observer design pattern • Well-suited for interactive systems
initiator Controller 1 repository * Model 1 notifier subscriber View * MVC Details
MVC Example Details 2:enterNewFileName(file,newName) 3:setName(newName) :Controller :Model 1:subscribeToFileEvents(file) 5:getName() :InfoView 4:notifySubscribedViews(file) 4:notifySubscribedViews(file) :FolderView 1:subscribeToFileEvents(file) 5:getName()
Paradigms Compared Application callbacks draw output Widgets Application output input input The User Traditional command-line GUI-based
Event loop – pseudocode int main() { return WinMain(); } WinMain() { while (1) { // loop forever, waiting for an event if (event_exists) { //there is an event, figure out what to do if (event == keydown_a) display(‘user pressed the A key’); else if (event == window_resize) display(‘window resized’); else if (event == repaint) display(‘need to repaint window’); else if (event == keydown_escape) exit_program(); } } }
Event loop – WinMain int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, char * cmdParam, int cmdShow) { char className [] = "Winnie"; WinClass winClass (WindowProcedure, className, hInst); winClass.Register (); WinMaker win ("Hello Windows!", className, hInst); win.Show (cmdShow); MSG msg; int status; while ((status = ::GetMessage (& msg, 0, 0, 0)) != 0) { if (status == -1) return -1; ::DispatchMessage (& msg); } return msg.wParam; }
Event loop – WindowProc LRESULT CALLBACK WindowProcedure (HWND hwnd, unsigned int message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_DESTROY: ::PostQuitMessage (0); return 0; } return ::DefWindowProc (hwnd, message, wParam, lParam ); }
Event loop (cont.) int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprev, PSTR cmdline, int ishow) { HWND hwnd; MSG msg; //initialization code goes here while(1) { // Get message(s) if there is one if(PeekMessage(&msg,hwnd,0,0,PM_REMOVE)) { if(msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); //this calls the CALLBACK function WinProc() } else { DrawScene(); //display the OpenGL/DirectX scene } } }
Event loop (cont.) LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { PAINTSTRUCT ps; // Depending on the message -- we'll do different stuff switch(message) { case WM_PAINT: Draw(); return 0; // ESC will quit program case WM_KEYDOWN: //user pressed a key if(GetAsyncKeyState(VK_ESCAPE)) //it was the escape key PostQuitMessage(0); //quit program return 0; case WM_DESTROY: //windows wants the program to die case WM_CLOSE: //or the user closed the window PostQuitMessage(0); //quit program return 0; } return DefWindowProc(hwnd, message, wparam, lparam); }
GUI Concepts • Widget – graphic object with functionality; e.g., button, toolbar, ... • Window – holds widgets • Child/parent – relationships between windows • Event / message – how windows communicate
Anatomy of a Window title bar menu toolbar client area status bar
Microsoft Windows Programming • History: • Win32 API: core library written in C • MFC: C++ wrappers around most common Win32 API functions • Lots of macros • Lots of legacy code • Not free, not portable • But it works (generally speaking)
Analyzing architectural designs Two approaches developed by SEI: • Architecture trade-off analysis method (ATAM) • Collect scenarios and requirements • Evaluate quality attributes and their sensitivity • Critique candidate architectures • Scenario-based architectural analysis (SAAM) • Uses scenarios to analyze architectures with respect to quality attributes Quality attributes: reliability, performance, security, maintainability, flexibility, testability, portability, reusability, interoperability
Component-level design • Occurs after the first iteration of architectural design • Goal: translate the design model into operational software • Component is set of collaborating classes • Designing components • OCL • flow chart • tabular design notation Decision table has four quadrants specifying conditions and actions, as well as rules for both • PDL (pseudocode)
Decomposition System ... Subsystem1 SubsystemN ... ... Class1a Class1n ClassNa ClassNn
Coupling and cohesion • Coupling -- # of dependencies between subsystems • Cohesion -- # of dependencies within subsystem • Goal: low coupling, high cohesion • 7 +/- 2 rule – keep number of concepts at any given layer of abstraction bounded
Additional design principles • Single responsibility (SRP)A class should have only one reason to change • Open-closed (OCP) Software entities should be open for extension but closed for modification (achieved via inheritance) • Liskov substitution (LSP)Subclasses should be substitutable for their base classes • Dependency inversion (DIP)Abstractions should not depend upon details • Interface segregation (ISP)Many client-specific interfaces are better than one general purpose interface • Release reuse equivalency (REP)Granule of reuse is granule of release • Common closure (CCP)Classes that change together belong together • Common reuse (CRP)Classes in a package are reused together
Data structures and flow • Software system is composed of • data structures, and • data flow • Which is more important? “Show me your code and conceal your data structures, and I shall continue to by mystified. Show me your data structures and I won’t usually need your code. It will be obvious.” – Fred Brooks
What is a Design Pattern? • A design pattern • abstracts a recurring design structure • comprises class and/or object • dependencies, • structures, • interactions, or • conventions • distills design experience
Re-use • Code re-use • Don’t reinvent the wheel • Requires clean, elegant, understandable, general, stable code • leverage previous work • Design re-use • Don’t reinvent the wheel • Requires a precise understanding of common, recurring designs • leverage previous work
Some design patterns • Abstract factory • Adapter • Bridge • Command • Composite • Façade • Subject / Observer • Proxy • Strategy
Subject-observer [from Vlissides]
Subject-observer (cont.) 1 * Subject Register(Observer) Unregister(Observer) NotifyAll() Observer OnUpdate() for all o in observers { o.OnUpdate() }
Subject-observer (cont.) 1 * Subject Register(Observer) Unregister(Observer) NotifyAll() Observer virtual OnUpdate() for all o in observers { o.OnUpdate() } ConcreteSubject ConcreteObserver virtual OnUpdate()
Model / view / controller (MVC) (displays data) View { Model m; Controller c(&m); View v(&c); } (mediates) Controller (holds data) Model calls Register() Main Create() View Create() OnUpdate() Register() Create() Set() Controller Set() Model
MVC (cont.) 1 * Subject Register(Observer) Unregister(Observer) NotifyAll() Observer virtual OnUpdate() for all o in observers { o.OnUpdate() } Controller View virtual OnUpdate()
MVC (cont.) class Observer { protected: virtual void OnUpdate(MsgId message_id) = 0; }; class Subject { public: enum MsgId {}; void RegisterObserver(Observer* obs); virtual void NotifyAllObservers(MsgId message_id) { for (int i=0 ; i<m_observers.size() ; i++) { m_observers[i]->OnUpdate(message_id); } } private: std::vector<Observer*> m_observers; };
MVC (cont.) class Controller : public Subject { Controller(Data* d) : m_data(d) {} const Data* GetData() const; void AddSphere(const Sphere& s) { m_data->AddSphere(s); NotifyAllObservers(ADD_SPHERE); } private: Data* m_data; };
MVC (cont.) class MainWnd : public Observer, CWnd { public: MainWnd(Controller* c) : m_controller(c) { c.Register(this); } virtual void OnUpdate(int message_id) { switch (message_id) { case Subject::ADD_SPHERE: ... } } private: Controller* m_controller; };
Adapter • You have • legacy code • current client • Adapter changes interface of legacy code so client can use it • Adapter fills the gap b/w two interfaces • No changes needed for either • legacy code, or • client
Adapter (cont.) class NewTime { public: int GetTime() { return m_oldtime.get_time() * 1000 + 8; } private: OldTime m_oldtime; };
Command • You have commands that need to be • executed, • undone, or • queued • Command design pattern separates • Receiver from Invoker from Commands • All commands derive from Command and implement do(), undo(), and redo()
Implementing ‘Undo/Redo’ • Multi-level undo/redo requires two classes: • Command • CommandManager class Command { public: virtual bool Execute() = 0; virtual bool Unexecute() = 0; virtual ~Command() { } }; class CommandManager { private: typedef list<Command*> CommandList; CommandList m_undoList; CommandList m_redoList; public: void DoCommand(Command* com); void Undo(); void Redo(); }; http://www.codeproject.com/KB/cpp/undoredo_cpp.aspx
Facade • You • have a set of related classes • want to shield the rest of the system from these details • Facade provides a simplified interface • Encapsulates a subsystem
Composite • You want uniformly to treat • items (atomic elements), and • groups (containing items or other groups) • Composite interface specifies operations that are shared between items and groups • Examples: hierarchy of files and directories, groups of drawable elements