Dependency Injection, Service Locator, Inversion of Control, Dependency Inversion Principle, … Viele Begriffe die irgendwie das Gleiche zu sagen scheinen und sich dann doch wieder unterscheiden. Sicher ist nur, dass es sich um lose Kopplung handelt. Dass wir dank dieser ominösen Begriffe wartbare Software erstellen können, die leichter zu erweitern ist und sich darüber hinaus auch noch gut testen lässt. Der Vortrag greift die zuvor genannten Begriffe auf und erläutert ihre Zusammenhänge. Dabei ist das Ziel mit altbekannten Vorurteilen aufzuräumen und bei der Entscheidung zu helfen wo Softwarebestandteile wirklich entkoppelt werden sollten und welche Antipatterns sich beim Praxiseinsatz ergeben können. Dabei sehen wir diverse Beispiele des Einsatzes der SOLID Prinzipien und warum Dependency Injection allein eben nicht zu besserer Software führt.
3. 1. It is hard to change because every change affects too many other parts
of the system. (Rigidity - Starr)
2. When you make a change, unexpected parts of the system break.
(Fragility - Zerbrechlich)
3. It is hard to reuse in another application because it cannot be
disentangled from the current application. (Immobility - Unbeweglich)
Quelle: http://www.objectmentor.com/resources/articles/dip.pdf
9. Robert C. Martin
(Uncle Bob)
The SOLID principles are not rules. They are not laws.
They are not perfect truths. They are statements on
the order of “An apple a day keeps the doctor away.”
This is a good principle, it is good advice, but it’s not a
pure truth, nor is it a rule.
“
”
Quelle: https://sites.google.com/site/unclebobconsultingllc/getting-a-solid-start
10. ingle Responsibility Principle
pen Closed Principle
iskov Substitution Principle
nterface Segregation Principle
ependency Inversion Principle
S
O
L
I
D
11. ingle Responsibility Principle
pen Closed Principle
iskov Substitution Principle
nterface Segregation Principle
ependency Inversion Principle
S
O
L
I
D
Eine Klasse sollte nur eine Verantwortlichkeit
haben.
Quelle: http://www.clean-code-developer.de/
12. ingle Responsibility Principle
pen Closed Principle
iskov Substitution Principle
nterface Segregation Principle
ependency Inversion Principle
S
O
L
I
D
Eine Klasse sollte offen für Erweiterungen, jedoch
geschlossen für Modifikationen sein.
Quelle: http://www.clean-code-developer.de/
13. ingle Responsibility Principle
pen Closed Principle
iskov Substitution Principle
nterface Segregation Principle
ependency Inversion Principle
S
O
L
I
D
Abgeleitete Klassen sollten sich so verhalten wie es
von ihren Basistypen erwartet wird.
Quelle: http://www.clean-code-developer.de/
14. ingle Responsibility Principle
pen Closed Principle
iskov Substitution Principle
nterface Segregation Principle
ependency Inversion Principle
S
O
L
I
D
Interfaces sollten nur die Funktionalität
wiederspiegeln die ihre Klienten erwarten.
Quelle: http://www.clean-code-developer.de/
15. ingle Responsibility Principle
pen Closed Principle
iskov Substitution Principle
nterface Segregation Principle
ependency Inversion Principle
S
O
L
I
D
High-Level Klassen sollen nicht von Low-Level Klassen abhängig
sein, sondern beide von Abstraktionen.
Abstraktionen sollen nicht von Details abhängig sein, sondern
Details von Abstraktionen.
Quelle: Wikipedia.org
16. High-Level Klassen sollen nicht von Low-Level
Klassen abhängig sein, sondern beide von
Abstraktionen.
Abstraktionen sollen nicht von Details abhängig sein,
sondern Details von Interfaces.
17. High-Level Klassen sollen nicht von Low-Level Klassen abhängig sein…
Klient
Leistungs-
träger
High-Level Low-Level
18. High-Level Klassen sollen nicht von Low-Level Klassen abhängig sein…
Klient
Leistungs-
träger
High-Level Low-Level
…
…
…
19. High-Level Klassen sollen nicht von Low-Level Klassen abhängig sein…
Person Bauer
High-Level Low-Level
Süßer
Apfel
21. High-Level Klassen sollen nicht von Low-Level
Klassen abhängig sein, sondern beide von
Abstraktionen.
Abstraktionen sollen nicht von Details abhängig sein,
sondern Details von Interfaces.
22. High-Level Klassen sollen nicht von Low-Level
Klassen abhängig sein, sondern beide von
Abstraktionen.
Abstraktionen sollen nicht von Details abhängig sein,
sondern Details von Interfaces.
25. High-Level Klassen sollen nicht von Low-Level
Klassen abhängig sein, sondern beide von
Abstraktionen.
Abstraktionen sollen nicht von Details abhängig sein,
sondern Details von Abstraktionen.
26. High-Level Klassen sollen nicht von Low-Level
Klassen abhängig sein, sondern beide von
Abstraktionen.
Abstraktionen sollen nicht von Details abhängig sein,
sondern Details von Abstraktionen.
40. public class StudentContext : DbContext
{
public StudentContext() : base()
{}
public DbSet<StudentEntity> Students { get; set; }
}
Creation
Inversion
41. public class StudentContext : DbContext, IStudentContext
{
public StudentContext() : base()
{}
public DbSet<StudentEntity> Students { get; set; }
}
public class IStudentContext
{
DbSet<StudentEntity> Students { get; set; }
}
Creation
Inversion
42. Creation
Inversion
public class StudentManagementViewModel
{
StudentContext context;
public StudentEntity Student { get; set; }
public StudentManagementViewModel()
{
this.context = new StudentContext();
}
public void ShowStudent(string name)
{
this.Student =
this.context.Students.FirstOrDefault<StudentEntity>
(s => s.StudentName == name);
}
}
43. Creation
Inversion
public class StudentManagementViewModel
{
IStudentContext context;
public StudentEntity Student { get; set; }
public StudentManagementViewModel()
{
this.context = ServiceLocator.Create<IStudentContext>();
}
public void ShowStudent(string name)
{
this.Student =
this.context.Students.FirstOrDefault<StudentEntity>
(s => s.StudentName == name);
}
}
44. Creation
Inversion
public class StudentManagementViewModel
{
IStudentContext context;
public StudentEntity Student { get; set; }
public StudentManagementViewModel(IStudentContext context)
{
this.context = context;
}
public void ShowStudent(string name)
{
this.Student =
this.context.Students.FirstOrDefault<StudentEntity>
(s => s.StudentName == name);
}
}
46. public class StudentContext : DbContext, IStudentContext
{
public StudentContext() : base()
{}
public DbSet<StudentEntity> Students { get; set; }
}
public class IStudentContext
{
DbSet<StudentEntity> Students { get; set; }
}
Interface
Inversion
47. public class IStudentRepository
{
IQueryable<StudentEntity> Students { get; set; }
}
Interface
Inversion
public class StudentContext : DbContext, IStudentRepository
{
public StudentContext() : base()
{
// ...
}
private DbSet<StudentEntity> students;
public IQueryable<StudentEntity> Students { get{ return this.students} }
}
48. [Table("StudentInfo")]
public class StudentEntity
{
public StudentEntity() { }
[Key]
public int SID { get; set; }
[Column("Name", TypeName = "ntext")]
[MaxLength(20)]
public string StudentName { get; set; }
[Column("BDate", TypeName = "datetime")]
public DateTime BirthDate { get; set; }
[NotMapped]
public int? Age { get;}
}
Interface
Inversion
49. public class Student
{
public int SID { get; set; }
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public int? Age { get; }
}
Interface
Inversion
public class IStudentRepository
{
IQueryable<Student> Students { get; set; }
}
[Table("StudentInfo")]
public class StudentEntity
{
[Key]
public int SID { get; set; }
[Column("Name", TypeName = "ntext")]
[MaxLength(20)]
public string StudentName { get; set; }
[Column("BDate", TypeName = "datetime")]
public DateTime BirthDate { get; set; }
}
50. Interface
Inversion
public class StudentManagementViewModel
{
IStudentContext context;
public StudentEntity Student { get; set; }
public StudentManagementViewModel(IStudentContext context)
{
this.context = context;
}
public void ShowStudent(string name)
{
this.Student =
this.context.Students.FirstOrDefault<StudentEntity>
(s => s.StudentName == name);
}
}
51. Interface
Inversion
public class StudentManagementViewModel
{
IStudentRepository repository;
public Student Student { get; set; }
public StudentManagementViewModel(IStudentRepository repository)
{
this.repository = repository;
}
public void ShowStudent(string name)
{
this.Student =
this.repository.Students.FirstOrDefault<Student>
(s => s.StudentName == name);
}
}
61. „The real problem is that programmers have spent far too
much time worrying about efficiency in the wrong places and
at the wrong times; premature optimization is the root of all
evil (or at least most of it) in programming.“
Quelle: https://en.wikiquote.org/wiki/Donald_Knuth
Donald Knuth
Auch das Dependency Inversion Principle (DIP) ist ein SOLID Prinzip. Es besagt folgendes: High-Level Klassen sollen nicht von Low-Level Klassen abhängig sein, sondern beide von Interfaces.
Interfaces sollen nicht von Details abhängig sein, sondern Details von Interfaces.
Verwendet eine High-Level Klasse eine Low-Level Klasse unmittelbar, so ergibt sich eine starke Kopplung zwischen beiden. Spätestens beim Versuch, die High-Level Klasse isoliert zu testen, wird man auf Schwierigkeiten stoßen. Aus diesem Grund sollte die High-Level Klasse von einem Interface abhängig sein, das wiederum von der Low-Level Klasse implementiert wird. So kann die Low-Level Klasse im Unit Test durch ein Mockup ersetzt werden.Um zur Laufzeit die invertierte, abstrakte Abhängigkeit mit einem konkreten Objekt aufzulösen, bieten sich im Prinzip drei Möglichkeiten: mittels Konstruktorparameter "per Hand"
Einsatz eines Inversion of Control Containers (IoC Container) wie etwa Castle Windsor
Dependency Lookup
Auch das Dependency Inversion Principle (DIP) ist ein SOLID Prinzip. Es besagt folgendes: High-Level Klassen sollen nicht von Low-Level Klassen abhängig sein, sondern beide von Interfaces.
Interfaces sollen nicht von Details abhängig sein, sondern Details von Interfaces.
Verwendet eine High-Level Klasse eine Low-Level Klasse unmittelbar, so ergibt sich eine starke Kopplung zwischen beiden. Spätestens beim Versuch, die High-Level Klasse isoliert zu testen, wird man auf Schwierigkeiten stoßen. Aus diesem Grund sollte die High-Level Klasse von einem Interface abhängig sein, das wiederum von der Low-Level Klasse implementiert wird. So kann die Low-Level Klasse im Unit Test durch ein Mockup ersetzt werden.Um zur Laufzeit die invertierte, abstrakte Abhängigkeit mit einem konkreten Objekt aufzulösen, bieten sich im Prinzip drei Möglichkeiten: mittels Konstruktorparameter "per Hand"
Einsatz eines Inversion of Control Containers (IoC Container) wie etwa Castle Windsor
Dependency Lookup
Auch das Dependency Inversion Principle (DIP) ist ein SOLID Prinzip. Es besagt folgendes: High-Level Klassen sollen nicht von Low-Level Klassen abhängig sein, sondern beide von Interfaces.
Interfaces sollen nicht von Details abhängig sein, sondern Details von Interfaces.
Verwendet eine High-Level Klasse eine Low-Level Klasse unmittelbar, so ergibt sich eine starke Kopplung zwischen beiden. Spätestens beim Versuch, die High-Level Klasse isoliert zu testen, wird man auf Schwierigkeiten stoßen. Aus diesem Grund sollte die High-Level Klasse von einem Interface abhängig sein, das wiederum von der Low-Level Klasse implementiert wird. So kann die Low-Level Klasse im Unit Test durch ein Mockup ersetzt werden.Um zur Laufzeit die invertierte, abstrakte Abhängigkeit mit einem konkreten Objekt aufzulösen, bieten sich im Prinzip drei Möglichkeiten: mittels Konstruktorparameter "per Hand"
Einsatz eines Inversion of Control Containers (IoC Container) wie etwa Castle Windsor
Dependency Lookup
Auch das Dependency Inversion Principle (DIP) ist ein SOLID Prinzip. Es besagt folgendes: High-Level Klassen sollen nicht von Low-Level Klassen abhängig sein, sondern beide von Interfaces.
Interfaces sollen nicht von Details abhängig sein, sondern Details von Interfaces.
Verwendet eine High-Level Klasse eine Low-Level Klasse unmittelbar, so ergibt sich eine starke Kopplung zwischen beiden. Spätestens beim Versuch, die High-Level Klasse isoliert zu testen, wird man auf Schwierigkeiten stoßen. Aus diesem Grund sollte die High-Level Klasse von einem Interface abhängig sein, das wiederum von der Low-Level Klasse implementiert wird. So kann die Low-Level Klasse im Unit Test durch ein Mockup ersetzt werden.Um zur Laufzeit die invertierte, abstrakte Abhängigkeit mit einem konkreten Objekt aufzulösen, bieten sich im Prinzip drei Möglichkeiten: mittels Konstruktorparameter "per Hand"
Einsatz eines Inversion of Control Containers (IoC Container) wie etwa Castle Windsor
Dependency Lookup
Auch das Dependency Inversion Principle (DIP) ist ein SOLID Prinzip. Es besagt folgendes: High-Level Klassen sollen nicht von Low-Level Klassen abhängig sein, sondern beide von Interfaces.
Interfaces sollen nicht von Details abhängig sein, sondern Details von Interfaces.
Verwendet eine High-Level Klasse eine Low-Level Klasse unmittelbar, so ergibt sich eine starke Kopplung zwischen beiden. Spätestens beim Versuch, die High-Level Klasse isoliert zu testen, wird man auf Schwierigkeiten stoßen. Aus diesem Grund sollte die High-Level Klasse von einem Interface abhängig sein, das wiederum von der Low-Level Klasse implementiert wird. So kann die Low-Level Klasse im Unit Test durch ein Mockup ersetzt werden.Um zur Laufzeit die invertierte, abstrakte Abhängigkeit mit einem konkreten Objekt aufzulösen, bieten sich im Prinzip drei Möglichkeiten: mittels Konstruktorparameter "per Hand"
Einsatz eines Inversion of Control Containers (IoC Container) wie etwa Castle Windsor
Dependency Lookup
Auch das Dependency Inversion Principle (DIP) ist ein SOLID Prinzip. Es besagt folgendes: High-Level Klassen sollen nicht von Low-Level Klassen abhängig sein, sondern beide von Interfaces.
Interfaces sollen nicht von Details abhängig sein, sondern Details von Interfaces.
Verwendet eine High-Level Klasse eine Low-Level Klasse unmittelbar, so ergibt sich eine starke Kopplung zwischen beiden. Spätestens beim Versuch, die High-Level Klasse isoliert zu testen, wird man auf Schwierigkeiten stoßen. Aus diesem Grund sollte die High-Level Klasse von einem Interface abhängig sein, das wiederum von der Low-Level Klasse implementiert wird. So kann die Low-Level Klasse im Unit Test durch ein Mockup ersetzt werden.Um zur Laufzeit die invertierte, abstrakte Abhängigkeit mit einem konkreten Objekt aufzulösen, bieten sich im Prinzip drei Möglichkeiten: mittels Konstruktorparameter "per Hand"
Einsatz eines Inversion of Control Containers (IoC Container) wie etwa Castle Windsor
Dependency Lookup
Was bringt uns Inversion of Control?
Entkopplung der Ausführung einer Aktion von ihrer Implementierung.
Fokussierung von Bestandteilen auf deren eigentliche Aufgabe.
Grundstrukturen werden durch Verträge festgehalten.
Zulieferer können einfacher durch andere ersetzt werden.