SoftwareArchitecture
Extracted and adapted from http://www.bredemeyer.com/pdf_files/ArchitectureDefinition.PDF
Architecture Views
Conceptual Architecture
The Conceptual Architecture identifies the high-level components of the system, and the relationships among them. Its purpose is to direct attention at an appropriate decomposition of the system without delving into details.
Goals:
- identification of components
- allocation of responsibilities to components
Logical Architecture
In Logical Architecture, the externally visible properties of the components are made precise and unambiguous through well-defined interfaces and component specifications, and key architectural mechanisms are detailed.
Goals:
- design of component interactions, connection mechanisms and protocols (exchanged messages);
- interface design and specification; providing contextual information for component users
Execution Architecture
An Execution Architecture is created for distributed or concurrent systems. The process view shows the mapping of components onto the processes of the physical system, with attention being focused on such concerns as throughput and scalability.
- assignment of the runtime component instances to processes,threads and address spaces;
- how they communicate and coordinate;
- how physical resources are allocated to them
UML simple steps
Reference: http://www.sparxsystems.com/resources/tutorial/uml_tutorial2.html
Steps:
- Create use cases based on the user goals and how those goals are achieved by interacting with the system
- Detail each use case with a best case scenario
- With the details of the use cases, begin to construct a domain model (high level business objects), sequence diagrams, collaboration/communication diagrams and user interface models. These describe the 'things' in the new system, the way those things interact and the interface a user will use to execute use case scenarios.
- With the domain model start to create a class model.
- The class model is a precise specification of the objects in the system, their data or attributes and their behaviour or operations.
- A component represents a deployable chunk of software that collects the behaviour and data of one or more classes and exposes a strict interface to other consumers of its services. So from the Class Model a Component Model is built to define the logical packaging of classes.
http://agilemodeling.com/essays/agileArchitecture.htm
http://www.sparxsystems.com.au/resources/uml2_tutorial/
http://agilemodeling.com/artifacts/useCaseDiagram.htm
http://agilemodeling.com/artifacts/crcModel.htm
http://agilemodeling.com/artifacts/robustnessDiagram.htm
Recipe 1 - Unified Process - UML - Use case driven
SSD (System Sequence Diagram) Use cases - Behavioral Requirements
- Identify use cases and create use case diagrams
- detail textually each use case (create use case detail)
- Create class diagram for domain model with only class names based on use case detail
- Create sequence/communication diagrams (SSD - 1st should be the System Sequence Diagram) that realizes/illustrates the use case
- Refine the classes diagram with the operations and attributes found in the sequence/communication diagram
Use case: Actors: Goals: Main scenario 1. The system ... 2. The user ...
Use case add numbers
@startuml actor user boundary system system -> user : prompts first number user -> system : inputs the first number system -> user : prompts for the second number user -> system : inputs the second number system -> user : shows the sum of the two numbers @enduml
1 '''
2 Use case: Add numbers
3 Actors: User
4 Goals: Add two numbers
5 Main scenario:
6 1. The system prompts for the first number
7 2. The user inputs the first number
8 3. The system prompts for the second number
9 4. The user inputs the second number
10 5. The system shows the sum of the two numbers
11
12 Extensions:
13 2.a The first number is invalid
14 2.a.1 The system shows the message "Please check the inputed value for the first value"
15 2.a.2 The use case continues at step 1
16
17 4.a The second number is invalid
18 4.a.1 The system shows the message "Please check the inputed value for the second value"
19 4.a.2 The use case continues at step 3
20
21 Postcondition: two numbers are added
22 '''
23 # python useCaseAdd.py
24 def readNumber(prompt,exceptionMessage):
25 gotValue=False
26 value=0
27 while(gotValue==False):
28 try:
29 value = int( input(prompt) )
30 gotValue=True
31 except Exception as ex:
32 print(exceptionMessage)
33 return value
34
35 first = readNumber('FirstNumber: ','Please check the inputed value for the first value')
36 second = readNumber('Second number: ','Please check the inputed value for the second value')
37 print('The sum is %d'%(first + second))
Add numbers SSD example
PlantUML online server: http://www.plantuml.com/plantuml/
@startuml actor user boundary system control business loop while first number is invalid system -> user : prompts first number user -> system : inputs the first number alt success system -> user : First number is valid else invalid first number system -> user : Please check the inputed value for the first value end end loop while second number is invalid system -> user : prompts second number user -> system : inputs the second number alt success system -> user : second number is valid else invalid second number system -> user : Please check the inputed value for the second value end end system -> business : add(first,second) business --> system : sum system -> user : shows the sum of the two numbers @enduml
Functional requirements (High level)
Define a high level API, design by contract, interface for the whole system.
Just one service with a lot of methods that help fullfil the use cases.
Developer point of view.
Functional requirement addNumber
In the above diagram the functional requirement FR is the add function that return a sum.
add(first:int,second:int):int
The system must be able to add two numbers and return the sum result.
SSD (System Sequence Diagram) implementation examples
ssdBoundary.py
1 #!/usr/bin/python3
2 """
3 All logic still in boundary/UI
4 """
5 class Entity:
6 """ """
7 def __init__(self):
8 pass
9
10 class Boundary:
11 """ """
12 def __init__(self):
13 pass
14
15 class Credencial(Entity):
16 """ """
17 def __init__(self,utilizador,password):
18 """ """
19 self.utilizador = utilizador
20 self.password = password
21
22 class Menu(Entity):
23 def __init__(self,nome):
24 """ """
25 self.nome=nome
26 self.opcoes=[]
27
28 def adicionarOpcao(self,opcao):
29 """ """
30 self.opcoes.append(opcao)
31
32 def getOpcoes(self):
33 """ """
34 return self.opcoes
35
36 class Opcao(Entity):
37 def __init__(self,nome,callback):
38 """ """
39 self.nome=nome
40 self.callback = callback
41
42 class Cotacao(Entity):
43 def __init__(self,nome,valor,data):
44 """ """
45 self.nome=nome
46 self.valor=valor
47 self.data=data
48
49 def __repr__(self):
50 """ """
51 return '%s %s %f'%(self.data, self.nome, self.valor)
52
53 class UI(Boundary):
54 def __init__(self):
55 """ """
56 self.menu=Menu('Menu')
57 self.menu.adicionarOpcao(Opcao('Cotações',self.cotacoes))
58 self.menu.adicionarOpcao(Opcao('Análises',self.analises))
59 self.menu.adicionarOpcao(Opcao('Sair',self.sair))
60
61 self.credenciais=[]
62 self.credenciais.append( Credencial('vitor','12345678') )
63 self.credenciais.append( Credencial('calvin','wally') )
64
65 self.cotacoes=[]
66 self.cotacoes.append(Cotacao('YHOO',1,'2016-01-02'))
67 self.cotacoes.append(Cotacao('CSCO',1,'2016-01-02'))
68 self.cotacoes.append(Cotacao('YHOO',2,'2016-01-03'))
69 self.cotacoes.append(Cotacao('CSCO',2,'2016-01-03'))
70
71
72 def pedirCredenciais(self):
73 """ """
74 print('Insira as suas credenciais')
75 print('Utilizador:')
76 utilizador = input()
77 print('Password:')
78 password = input()
79 return Credencial(utilizador,password)
80
81 def validarCredenciais(self,credencial):
82 """ """
83 found=False
84 for c in self.credenciais:
85 if(credencial.utilizador==c.utilizador and credencial.password==c.password):
86 found=True
87
88 if found:
89 print('Credenciais válidas')
90 return True
91 else:
92 print('Credenciais inválidas')
93 return False
94
95 def mostrarMenu(self):
96 for opcao in self.menu.getOpcoes():
97 print(' %s'%(opcao.nome))
98 opcao=input()
99 selOpcao=None
100 for o in self.menu.getOpcoes():
101 if o.nome==opcao:
102 selOpcao=o
103 return selOpcao
104
105 def cotacoes(self):
106 print('Menu Cotações')
107 cots=[]
108 for c in self.cotacoes:
109 if c.nome not in cots:
110 cots.append(c.nome)
111 print(c.nome)
112 print('Escolha a cotação')
113 cotacao=input()
114 #mostrar cotacões para acção escolhida
115 for c in self.cotacoes:
116 if c.nome == cotacao:
117 print(c)
118
119 def analises(self):
120 print('Menu Análises')
121
122 def sair(self):
123 exit(0)
124
125 if __name__=='__main__':
126 ui = UI()
127 credencial = ui.pedirCredenciais()
128 resultado = ui.validarCredenciais(credencial)
129
130 if resultado==True:
131 opcao = ui.mostrarMenu()
132 opcao.callback()
ssdControl.py
1 #!/usr/bin/python3
2 """
3 All logic in control/service classes
4 """
5 class Entity:
6 """ Model """
7 def __init__(self):
8 pass
9
10 class Boundary:
11 """ View """
12 def __init__(self):
13 pass
14
15 class Control:
16 """ Business Logic - Controller """
17 def __init__(self):
18 pass
19
20 class Credencial(Entity):
21 """ """
22 def __init__(self,utilizador,password):
23 """ """
24 self.utilizador = utilizador
25 self.password = password
26
27 class Menu(Entity):
28 def __init__(self,nome):
29 """ """
30 self.nome=nome
31 self.opcoes=[]
32
33 def adicionarOpcao(self,opcao):
34 """ """
35 self.opcoes.append(opcao)
36
37 def getOpcoes(self):
38 """ """
39 return self.opcoes
40
41 class Opcao(Entity):
42 def __init__(self,nome,callback):
43 """ """
44 self.nome=nome
45 self.callback = callback
46
47 class Cotacao(Entity):
48 def __init__(self,nome,valor,data):
49 """ """
50 self.nome=nome
51 self.valor=valor
52 self.data=data
53
54 def __repr__(self):
55 """ """
56 return '%s %s %f'%(self.data, self.nome, self.valor)
57
58 class ControloCredencial(Control):
59 def __init__(self):
60 self.credenciais=[]
61 self.credenciais.append( Credencial('vitor','********') )
62 self.credenciais.append( Credencial('calvin','*') )
63
64 def validarCredenciais(self,credencial):
65 """ """
66 found=False
67 for c in self.credenciais:
68 if(credencial.utilizador==c.utilizador and credencial.password==c.password):
69 found=True
70
71 if found:
72 print('Credenciais válidas')
73 return True
74 else:
75 print('Credenciais inválidas')
76 return False
77
78 class ControloCotacoes(Control):
79 def __init__(self):
80 """ """
81 self.cotacoes=[]
82 self.cotacoes.append(Cotacao('YHOO',1,'2016-01-02'))
83 self.cotacoes.append(Cotacao('CSCO',1,'2016-01-02'))
84 self.cotacoes.append(Cotacao('YHOO',2,'2016-01-03'))
85 self.cotacoes.append(Cotacao('CSCO',2,'2016-01-03'))
86
87 def obterNomesAccoes(self):
88 """ """
89 accoes=[]
90 for c in self.cotacoes:
91 if c.nome not in accoes:
92 accoes.append(c.nome)
93 return accoes
94
95 def obterCotacoesParaAccao(self,accao):
96 """ """
97 cotacoesPorAccao=[]
98 for c in self.cotacoes:
99 if c.nome == accao:
100 cotacoesPorAccao.append(c)
101 return cotacoesPorAccao
102
103 class ControloMenus(Control):
104 def __init__(self):
105 """ """
106 self.menu=Menu('Menu')
107 self.menu.adicionarOpcao(Opcao('Cotações','cotacoes'))
108 self.menu.adicionarOpcao(Opcao('Análises','analises'))
109 self.menu.adicionarOpcao(Opcao('Sair','sair'))
110
111 def getMenu(self):
112 """ """
113 return self.menu
114
115 def obterOpcaoPorNome(self,nomeOpcao):
116 """ """
117 selOpcao=None
118 for o in self.menu.getOpcoes():
119 if o.nome==nomeOpcao:
120 selOpcao=o
121 return selOpcao
122
123 class UI(Boundary):
124 """ Lembrar de SSD System Sequence Diagram/Black Box"""
125 def __init__(self):
126 """ """
127 self.controloMenus = ControloMenus()
128 self.controloCredencial = ControloCredencial()
129 self.controloCotacoes = ControloCotacoes()
130
131 def pedirCredenciais(self):
132 """ """
133 print('Insira as suas credenciais')
134 print('Utilizador:')
135 utilizador = input()
136 print('Password:')
137 password = input()
138 return Credencial(utilizador,password)
139
140 def validarCredenciais(self,credencial):
141 """ """
142 return self.controloCredencial.validarCredenciais(credencial)
143
144 def mostrarMenu(self):
145 for opcao in self.controloMenus.getMenu().getOpcoes():
146 print(' %s'%(opcao.nome))
147 opcao=input()
148
149 selOpcao=self.controloMenus.obterOpcaoPorNome(opcao)
150 mapa={'cotacoes':self.cotacoes,'analises':self.analises,'sair':self.sair}
151 # define referencia de metodo conforme o nome de callback
152 selOpcao.callback = mapa[selOpcao.callback]
153 return selOpcao
154
155 def cotacoes(self):
156 print('Menu Cotações')
157
158 for nomeAccao in self.controloCotacoes.obterNomesAccoes():
159 print(nomeAccao)
160
161 print('Escolha a cotação')
162 cotacao=input()
163 for c in self.controloCotacoes.obterCotacoesParaAccao(cotacao):
164 print(c)
165
166 def analises(self):
167 print('Menu Análises')
168
169 def sair(self):
170 exit(0)
171
172 if __name__=='__main__':
173 ui = UI()
174 credencial = ui.pedirCredenciais()
175 resultado = ui.validarCredenciais(credencial)
176
177 if resultado==True:
178 opcao = ui.mostrarMenu()
179 opcao.callback()
SOLID
Single responsibility principle
a class should have only a single responsibility
Open/closed principle
open for extension, but closed for modification. Decorator pattern add new responsibilities, behavior. final class java. sealed csharp class.
Liskov substitution principle
design by contract using interfaces, have loose coupling by using interfaces
Interface segregation principle
"many client-specific interfaces are better than one general-purpose interface."
Dependency inversion principle
depend upon abstractions (abstract classes or interfaces) not concrete stuff
Software design patterns
Singleton
Ensure a class has only one instance, and provide a global point of access to it
Decorator
Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality. (SOLID O principle, open for extension closed for modification
Iterator
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. next() method. IEnumerable in Csharp. linked list, double linked list.
Memento
capture and externalize an object's internal state allowing the object to be restored to this state later. persist state and restore later (history state, undo).
Null object
Avoid null references by providing a default object. (Java8 Optional<T>, Csharp Nullable<T> )
Observer or Publish/subscribe
Define a one-to-many dependency between objects where a state change in one object results in all its dependents being notified and updated automatically. attach() notify() update()
Strategy
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. sorting algorhitms, hide used algorithm quicksort