3. Promote object orient programming
and design
Promote code reuse
Prevent duplicated code
Promote adaptive software
development
Keep things simple + YAGNI
4. Basic principles
• Degree to which each program
module relies on each one of
the other modules.
Coupling
• Degree to which elements
belong together.
Cohesion
• Make objects interchangeable,
and guards their states from
invalid changes
Encapsulation
6. Coupling
public void DoSomething() {
// Go get some configuration
int threshold =
int.Parse(ConfigurationManager.AppSettings["thr
eshold"]);
string connectionString =
ConfigurationManager.AppSettings["connectionSt
ring"];
string sql =
@"select * from things
size > ";
sql += threshold;
using (SqlConnection connection =
new SqlConnection(connectionString)) {
connection.Open();
SqlCommand command = new
SqlCommand(sql, connection);
using (SqlDataReader reader =
command.ExecuteReader()) {
while (reader.Read()) {
string name = reader["Name"].ToString();
string destination =
reader["destination"].ToString();
// do some business logic in here
doSomeBusinessLogic(name, destination,
connection);
} ….} }
7. Coupling
public void DoSomething() {
// Go get some configuration
int threshold =
int.Parse(ConfigurationManager.AppSettings["thr
eshold"]);
string connectionString =
ConfigurationManager.AppSettings["connectionSt
ring"];
string sql =
@"select * from things
size > ";
sql += threshold;
using (SqlConnection connection =
new SqlConnection(connectionString)) {
connection.Open();
SqlCommand command = new
SqlCommand(sql, connection);
using (SqlDataReader reader =
command.ExecuteReader()) {
while (reader.Read()) {
string name = reader["Name"].ToString();
string destination =
reader["destination"].ToString();
// do some business logic in here
doSomeBusinessLogic(name, destination,
connection);
} ….} }
21. I - Interface segregation principle (ISP)
Clients should not be forced to implement
interfaces they don’t use.
Or as Uncle Bob puts it: Make fine grained
interfaces that are client specific.
24. D - Dependency inversion principle
(DIP)
high-level classes should not depend on low-
level classes. Both should depend on
abstractions.
Abstractions should not depend on details.
Details should depend on abstractions.
25. Dependency inversion principle
public class BirthdayCalculator
{
private readonly List<Birthday> _birthdays;
public BirthdayCalculator()
{
_birthdays = new List<Birthday>();
}
public List<Birthday> Birthdays
{
get { return _birthdays; }
}
public List<Birthday> GetTodaysBirthdays()
{
return _birthdays
.Where(bd => bd.Date.Month == DateTime.Now.Date.Month)
.Where(bd => bd.Date.Day == DateTime.Now.Date.Day)
.ToList();
}
}
26. Dependency inversion principle
public class BirthdayCalculator
{
private readonly List<Birthday> _birthdays;
public BirthdayCalculator()
{
_birthdays = new List<Birthday>();
}
public List<Birthday> Birthdays
{
get { return _birthdays; }
}
public List<Birthday> GetTodaysBirthdays()
{
return _birthdays
.Where(bd => bd.Date.Month == DateTime.Now.Date.Month)
.Where(bd => bd.Date.Day == DateTime.Now.Date.Day)
.ToList();
}
}
27. Dependency inversion principle
public class BirthdayCalculator
{
private readonly IList<Birthday> _birthdays;
public BirthdayCalculator(IList<Birthday> birthdays)
{
_birthdays = birthdays;
}
public IList<Birthday> Birthdays
{
get { return _birthdays; }
}
public IList<Birthday> GetBirthdays(DateTime checkDate)
{
return _birthdays
.Where(bd => bd.Date.Day == checkDate.Day)
.Where(bd => bd.Date.Month == checkDate.Month)
.ToList();
}
}
28. QUIZ 1/5
A class should have one, and only one,
reason to change.
SRP
29. QUIZ 2/5
You should be able to extend a classes
behavior, without modifying it.
OCP
30. QUIZ 3/5
References to base classes must be able to use
objects of derived classes without knowing it
LSP
39. Correlations
• SRP correlates with the DIP since the abstraction
of responsibilities is often reached by inversion of
dependecies.
• OCP correlates with the DIP since the openness
for change is opften reached by inversion of
dependecies.
• DIP correlates with OCP, since there is the chance
that new requirements could be satisfied by
adding details to existing abstractions.
Editor's Notes
S.O.L.I.D. is a collection of best-practice object-oriented design principles that you can apply to your design to accomplish various desirable goals like loose-coupling, higher maintainability, intuitive location of interesting code, etc. S.O.L.I.D. is an acronym for the following principles (which are, themselves acronyms — confused yet?).
עקרונות בסיסיים:
Cohesion
refers to the degree to which the elements of a module belong together
לכידות–
מדד לכידות (באנגלית: Cohesion) מייצג את חוזק הקשר הפונקציונלי בין פעולות שונות תחת אותו מודול. מונח זה הומצא על ידי מהנדס תוכנה אמריקאי בשם לארי קונסטנטין, כחלק משיטה שפיתח להערכת איכות של תוכנה. מונח נוסף אשר הומצא במסגרת שיטה זו הוא מדד הצימוד (Coupling), המתייחס לרמת התלות בין המודולים השונים. לכידות גבוהה, במקרים רבים, היא סימן לצמידות נמוכה ולהיפך.
בהערכת איכותו של קוד תוכנה, יועדף קוד בעל לכידות גבוהה, המהווה סימן לתכונות חיוביות וחשובות שעל הקוד לקיים, הכוללות חוסן, אמינות, יכולת שימוש מחדש ויכולת הבנה גבוהה. מאידך, לכידות נמוכה מרמזת על תכונות שליליות הכוללות קושי בתחזוקה, קושי בבדיקות, יכולת שימוש מחדש נמוכה ויכולת הבנה נמוכה.
Coupling
Given two lines of code, A and B, they are coupled when B must change behavior only because A changed.
coupling or dependency is the degree to which each program module relies on each one of the other modules.
צימוד –
בהערכת איכותו של קוד תוכנה, יועדף קוד בעל צמידות נמוכה, המהווה סימן לכך שהקוד תוכנן כהלכה ובנוי היטב. על מנת להשיג צמידות נמוכה, יש לתכנן כל מודול כיחידה עצמאית ככל הניתן, אשר תלויה כמה שפחות במודולים נוספים במערכת. במצב זה, במידה שיידרשו שינויים בקוד, אלו יבוצעו ביתר קלות כיוון ששינוי במודול מסוים לא יאלץ שינויים בכל המערכת.
שילוב של צמידות נמוכה ולכידות גבוהה הוא סימן לכך שהקוד בעל יכולת תחזוקה גבוהה, מצביע על חוסן, אמינות, יכולת שימוש מחדש ויכולת הבנה גבוהה של הקוד.
Encapsulation
enclosing objects in a common interface in a way that makes them interchangeable, and guards their states from invalid changes
כימוס
Coupling refers to the degree to which one class knows about or uses members of another class.Loose coupling is the desirable state of having classes that are well encapsulated, minimize references to each other, and limit the breadth of API usage.Tight coupling is the undesirable state of having classes that break the rules of loose coupling.Cohesion refers to the degree in which a class has a single, well-defined role or responsibility.High cohesion is the desirable state of a class whose members support a single, well-focused role or responsibility.Low cohesion is the undesirable state of a class whose members support multiple, unfocused roles or responsibilities.
ניתן לסווג קוד לאחת מרמות הסיווג הבאות (מהטובה ביותר אל הגרועה ביותר):
לכידות פונקציונלית (Functional Cohesion) - פעולות במודול תורמות כולן למשימה בודדת ומוגדרת היטב של המודול.
לכידות סדרתית (Sequential Cohesion) - פעולות במודול מקובצות תחתיו כיוון שהפלט של פעולות מסוימות הינו הקלט של פעולות אחרות.
לכידות תקשורתית (Communicational Cohesion) - פעולות במודול מקובצות תחתיו כיוון שהן פועלות על אותו המידע.
לכידות פרוצדורלית (Procedural Cohesion) - פעולות במודול מקובצות תחתיו כיוון שהן תמיד מבוצעות יחד כחלק מהליך של המערכת (לדוגמה, פעולת בקשת הרשאות ופעולת פתיחת קובץ המבוצעות בזו אחר זו).
לכידות טמפורלית (Temporal Cohesion) - כאשר הפעולות במודול מקובצות תחתיו כיוון שהן מבצעות את פעולותיהן במהלך פרק זמן מוגדר במהלך ריצת התוכנית.
לכידות לוגית (Logic Cohesion) - כאשר הפעולות במודול מקובצות תחתיו כיוון שהן מבצעות משימות דומות, גם כאשר הינן שונות מטבען (לדוגמה, קיבוץ כל פעולות הקלט תחת אותו מודול).
לכידות מקרית (Coincidental Cohesion) - הפעולות במודול מקובצות תחתיו באופן שרירותי, כאשר הקשר היחיד בין הפעולות הוא בכך שקובצו יחדיו תחת אותו מודול.
Coincidental cohesion (worst)
Coincidental cohesion is when parts of a module are grouped arbitrarily; the only relationship between the parts is that they have been grouped together (e.g. a “Utilities” class).
Logical cohesion
Logical cohesion is when parts of a module are grouped because they logically are categorized to do the same thing, even if they are different by nature (e.g. grouping all mouse and keyboard input handling routines).
Temporal cohesion
Temporal cohesion is when parts of a module are grouped by when they are processed - the parts are processed at a particular time in program execution (e.g. a function which is called after catching an exception which closes open files, creates an error log, and notifies the user).
Procedural cohesion
Procedural cohesion is when parts of a module are grouped because they always follow a certain sequence of execution (e.g. a function which checks file permissions and then opens the file).
Communicational cohesion
Communicational cohesion is when parts of a module are grouped because they operate on the same data (e.g. a module which operates on the same record of information).
Sequential cohesion
Sequential cohesion is when parts of a module are grouped because the output from one part is the input to another part like an assembly line (e.g. a function which reads data from a file and processes the data).
Functional cohesion (best)Functional cohesion is when parts of a module are grouped because they all contribute to a single well-defined task of the module (e.g. tokenizing a string of XML).
This code is of low cohesion – too many things are involved within the same method.
מושגים ע"ש רוברט סי מרטין (הידוע בשם "הדוד בוב")
הרציונל : צריך להיות מקום אחד בקוד שצריך לשנות על מנת לשנות התנהגות. המקום הנכון הוא כזה שמונע תופעת לוואי.
אם קרה שבשל שינוי אחד – עברתם על מספר מקומות בקוד – אתם כנראה לאט אוכפים נכון SRP
SRP is the idea that objects should do one thing and one thing only. It is tempting to think this means that a class should represent an entire piece of business logic or data, but that is not quite correct. The idea is not to group all of the functionality that is related together, but all of the functionality that achieves the same goal together.
For example, if you have a class representing printer, putting all of the printer operations (getting toner levels, printing a page, getting an error message) in the printer class may make sense, but it is not really the right approach. Instead, you would put all of the functions related to printing in the same class, and all of the functions for reporting status in a class, and so on. You break the work down into small, digestible chunks. The payoff here is that you can make changes to a select piece of functionality without possibly affecting a ton of other items.
- Uncle Bob defines a responsibility to be ‘a reason for change’. The more responsibilities a class has, the more change can be expected. The more expected change, the more likely is the introduction of bugs. Changes to one responsibility can have an impact on other responsibilities.
רדיו מכיל מספר תפקידים
The first post in the SOLID by Example series deals with the Single Responsibility Principle (SRP). This is the notion that an object should have only a single responsibility. Uncle Bob defines a responsibility to be ‘a reason for change’. The more responsibilities a class has, the more change can be expected. The more expected change, the more likely is the introduction of bugs. Changes to one responsibility can have an impact on other responsibilities.
In this example we create a Radio class with some common operations. We can turn the volume up or down and change the station. Here is one way we could implement this class.(left)
One could argue that the Radio class has two responsibilities, being volume and station management. These operations will be called from completely different areas of the client using it. Changes can occur whenever we want to modify some of the volume- or station management logic. By separating these responsibilities into distinct classes, we can avoid breakage in other parts of our class when our code meets a new requirement (next)
הפרדת התפקידים.
האפקט:
הקטנת תופעות לוואי בעת שינוי קוד
פחות מקומות שמשנים בקוד על מנת לשנות התנהגות
Increase reusability
כשמגיעה דרישה חדשה – למעשה לא מיד צריך לשנות את מה שקיים. למעשה אולי כדאי שדרישות חדשות ימומושו בקוד חדש (extension)\
לא ניתן לתכנן מערכת שהיא פתוחה לענות על כל הדרישות.
----------------
“Open chest surgery is not needed when putting on a coat”.
This very simple idea is that classes can allow themselves to be extended but not modified. The source code for the original class should only be modified if a bug is found. The reason behind this is that changing the code will mean that everything depending upon it will need to be retested as well.
Shape בנוי בצורה שאינה מאפשרת הרחבה. כל שינוי בSHAPE יוביל לשינוי ביורשים ובמשתמשים. במבנה כזה עדיף לערוך או להוסיף קלאס חדש.
כמו כן – drawshape הוא מעין switch/case ארוך שצריך לתחזק.
Left
{
03 public void DrawShape(Shape s)
04 {
05 if (s._type == 1)
06 {
07 DrawRectangle((Rectangle)s);
08 }
09 else if (s._type == 2)
10 {
11 DrawCircle((Circle)s);
12 }
13}
14
15 public void DrawRectangle(Rectangle s)
16 {
17 Console.WriteLine("Rectangle");
18 }
19
20 public void DrawCircle(Circle s)
21 {
22 Console.WriteLine("Circle");
23 }
24}
הפונקציונל מוגדר בINTERFACE
אין צורך בswitch case
קל להרחיב את המודל – ע"י הוספת class שיורש מshape
האפקט:
נמנעים משינוי בהתנהגות המקורית של המערכת
מקטינים את כמות הקוד שנוגעים בו ע"מ לשנות.
----------------\
ברברה ליסקוב
Let q(x) be property provable about object x of type T. Then q(y) should be provable for objects y of type S where S is a subtype f T.
רציונל:
הורשה אינה מקיימת יחס is-a . LSP מקיים יחס "מתנהג כמו" behaves-like-a
היררכיה צריכה להיות מוגדרת כך שרמות נמוכות מרחיבות שימוש של רמות שמעליהן.
square: width and hight are the same
Rectangle:width and height vary independently
מה שקורה כאן = ששימוש (או החלפה) של SQUARE בRETANGLE תגרות לתופעות בלתי צפויות.
The Liskov Substitution Principle is broken here, because the behavior of the Width and Height properties changed in the descendant class. Substitution of the Square with a Rectangle gives some unexpected results.
(
That test will fail, because setting a square's width to 100 will also change its height.
Thus, Liskov's substitution principle is violated by deriving Square from Rectangle.
The "is-a" rule makes sense in the "real world" (a square is definitely a kind of rectangle), but not always in the world of software design.
square: width and hight are the same
Rectangle:width and height vary independently
מה שקורה כאן = ששימוש (או החלפה) של SQUARE בRETANGLE תגרות לתופעות בלתי צפויות.
The Liskov Substitution Principle is broken here, because the behavior of the Width and Height properties changed in the descendant class. Substitution of the Square with a Rectangle gives some unexpected results.
(
באפקט :
Code is “well formed”
נמנעים מתופעות לוואי בעת מימוש הרחבות.
The Liskov Substitution Principle held here, because the behavior of the Width and Height properties were not changed in the descendant class. Substitution of the Square or Rectangle with the Shape class gives the expected result.
ISP reduces coupling.
SEGREGATION – הפרדה\הבדלה
High coupling between person and calendar.
לCALENDAR נכנס מידע שאולי לא רלוונטי כמו משקל וגובה.
In the following example we have a simple Person class with some obvious person-like methods and properties. We also have a BirthdayCalendar class that has an Add() method to add a person’s birthday to the calendar. The Add() method takes a Person object as a parameter.
This makes sure that Person and BirtdayCalendar are tightly coupled. If Person changes, this can have an impact. If we want to add birtdays from entities other than from a Person we’re in trouble. There’s also no need for BirtdayCalendar to know all of Persons interface.
This is why some might say that this class has a fat interface. It has a few useless properties and methods for different clients using the interface. To prevent this we let BirthdayCalendar specify an interface that Person must implement. In this case IBirthday with a Name, Age and DayOfBirth property.
לCALENDAR נכנס רק המידע הרלוונטי.
האפקט:
Avoid unwanted side effects
Increase reusability.
This makes BirthdayCalendar independent of the Person class which makes it easier to maintain and extend. Imagine we also wanted to add the birthdays of pets to the BirthdayCalendar. The Pet class only had to implement the IBirtday interface to achieve that.
העיקרון מדבר על שינוי דרישות קיימות. אם הפרטים משתנים, לא צריך לשנות את כל המערכת.
הפרטים :
List
Hardcoded DataTime.Now.Date.Month
הפרטים :
List
Hardcoded DataTime.Now.Date.Month
במקום list – משתמשים באבסטרקציה
dateTime מןעבר כפרמטר.
האפקט:
Reduce effort when adapting existing code when modifying existing code.
Avoid unwanted side effects
Increase reusability.
There should only be one reason for a class to change (SRP)
Entities should be open for extension but close for modification (OCP)
References to base classes must be able to use objects of derived classes without knowing it (LSP)
Clients should not be forced to depend upon interfaces that they use (ISP)
Depend upon abstractions. Do not depend upon concretions(DIP)
There should only be one reason for a class to change (SRP)
Entities should be open for extension but close for modification (OCP)
References to base classes must be able to use objects of derived classes without knowing it (LSP)
Clients should not be forced to depend upon interfaces that they use (ISP)
Depend upon abstractions. Do not depend upon concretions(DIP)
There should only be one reason for a class to change (SRP)
Entities should be open for extension but close for modification (OCP)
References to base classes must be able to use objects of derived classes without knowing it (LSP)
Clients should not be forced to depend upon interfaces that they use (ISP)
Depend upon abstractions. Do not depend upon concretions(DIP)
There should only be one reason for a class to change (SRP)
Entities should be open for extension but close for modification (OCP)
References to base classes must be able to use objects of derived classes without knowing it (LSP)
Clients should not be forced to depend upon interfaces that they use (ISP)
Depend upon abstractions. Do not depend upon concretions(DIP)
There should only be one reason for a class to change (SRP)
Entities should be open for extension but close for modification (OCP)
References to base classes must be able to use objects of derived classes without knowing it (LSP)
Clients should not be forced to depend upon interfaces that they use (ISP)
Depend upon abstractions. Do not depend upon concretions(DIP)