Introduction

  • Design patters are common architecture approaches. (是架构的设计的具体应用方法论)
  • Students in Software Engineering Major are usually requirde to take the design patterns in their 3rd year program.
  • Popularized by the Gang of Fours book, 1994 – Smalltalk & C++
  • Translated to many OOP languages: C#, Jav

gbkQcf

Course Structure

XDVYku

Creational (创建型)

YUfWuB

Structual (结构型)

atFs0y

Behavioral (行为型)

eE5Zkn

About Me

  • Software Coach in 2012 Software Engineering Lab

The SOLID Design Principles

SOLID is an acronym which stands for the following design principles (and their abbreviations):

• Single Responsibility Principle (SRP)

• Open-Closed Principle (OCP)

• Liskov Substitution Principle (LSP)

• Interface Segregation Principle (ISP)

• Dependency Inversion Principle (DIP)

  • Was introduced by Robert C. Martin
  • I will reference from these books

CrjDKq

NViQJZ

Single Responsibility Principle (单一功能原则)

The single-responsibility principle (SRP) is a computer-programming principle that states that every module or class[1] should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class, module or function.

面向对象编程领域中,单一功能原则(Single responsibility principle)规定每个类都应该有一个单一的功能,并且该功能应该由这个类完全封装起来。所有它的(这个类的)服务都应该严密的和该功能平行(功能平行,意味着没有依赖)。

Example

Supposed we created a notebook which provides a function to add a new note to the notebook and persist all the entries to a file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <fstream>
#include <boost/lexical_cast.hpp>
using namespace std;
using namespace boost;

struct NoteBook
{
string title;
vector<string> entries;
//constructor
NoteBook(const string &title) : title(title) {}

//add a note the notebook
void add_note(const string& note){
static int count = 1;
entries.push_back(lexical_cast<string>(count) + ": " + title);
count++;
}

//what if you want to let the Notebook to save the entries to a file
void save(const string& filename)
{
//Open a file output steam
ofstream ofs(filename);
//write all the note entries to the file
for(auto& entry:entries){
ofs << entry << endl;
}
}
};

int main() {
std::cout << "Single Responsibility Principle Example" << std::endl;
NoteBook notebook{"Study Diligently"};
notebook.add_note("We need to understand the principles.");
notebook.add_note("We need to do lots of hands-on work!");
//Single Responsibility Principle is broken
notebook.save("notebook.txt");
return 0;
}

However, the save function above raises a problem. Sooner in the future, the the system starts to add more media types such as Journal and BookMark, which has its own Save and Load function.

EECb2t

Later the product manager decides to save the records into the the database instead of a file system.

SHEI1E

This migration causes tremendous amount changes of the code because engineers have to go to each sub module, NoteBook, Journal and BookMark to change the Save and Load function. To avoid this problem, we essentially use the seperation of concerns.(忧虑分离)

qkzDyf

In this example, to resovle this concern, we separate the pesistence functiionality to a third class, call PersistenceConnector.

1
2
3
4
5
6
7
8
9
10
struct PersistenceConnector
{
static void save(const NoteBook& noteBook, const string& filename){
ofstream ofs(filename);
//write all the note entries to the file
for(auto& entry:noteBook.entries){
ofs << entry << endl;
}
}
};

This is precisely what is meant by Single Responsibility: each class has only one responsibility, and therefore has only one reason to

change. On the other hand, if you wanted to change the persistence mechanic, this would be changed in PersistenceConnector.

Instead of using the save function from the NoteBook class, we can use the PersistenceConnector to handle hte responsiblity of the persistence. Now, we have the final version of the code with considering the Single Responsibility Principle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <fstream>
#include <boost/lexical_cast.hpp>

using namespace std;
using namespace boost;

struct NoteBook {
string title;
vector<string> entries;

//constructor
NoteBook(const string &title) : title(title) {}

//add a note the notebook
void add_note(const string &note) {
static int count = 1;
entries.push_back(lexical_cast<string>(count) + ": " + note);
count++;
}

//what if you want to let the Notebook to save the entries to a file
void save(const string &filename) {
//Open a file output steam
ofstream ofs(filename);
//write all the note entries to the file
for (auto &entry:entries) {
ofs << entry << endl;
}
}
};

struct PersistenceConnector {
static void save(const NoteBook &noteBook, const string &filename) {
ofstream ofs(filename);
//write all the note entries to the file
for (auto &entry:noteBook.entries) {
ofs << entry << endl;
}
}
};


int main() {
std::cout << "Single Responsibility Principle Example" << std::endl;
NoteBook notebook{"Study Diligently"};
notebook.add_note("We need to understand the principles.");
notebook.add_note("We need to do lots of hands-on work!");

//Single Responsibility Principle is broken
//notebook.save("notebook.txt");
//Instead, we use the PersistenceConnector to handle the persistence.
PersistenceConnector pc;
pc.save(notebook, "notebook.txt");
return 0;
}

An extreme example of an antipattern that violates the SRP is called a God Object. A God Object is a huge class that tries to handle as many concerns as possible, becoming a monolithic monstrosity that is very difficult to work with.