{"id":529,"date":"2023-01-27T10:00:00","date_gmt":"2023-01-27T09:00:00","guid":{"rendered":"https:\/\/rising-bits.com\/?p=529"},"modified":"2023-01-26T16:17:15","modified_gmt":"2023-01-26T15:17:15","slug":"solid-mit-typescript","status":"publish","type":"post","link":"https:\/\/rising-bits.com\/en\/solid-mit-typescript\/","title":{"rendered":"SOLID mit TypeScript"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">TypeScript hat bereits einen gro\u00dfen Einfluss darauf, dass man dem Clean-Code-Gedanken besser erf\u00fcllt. Doch man kann mit dem SOLID Prinzip noch viel besseren Programmcode schreiben. In diesem Artikel erkl\u00e4re ich anhand von Beispielen SOLID in TypeScript.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">SOLID ist ein Akronym f\u00fcr f\u00fcnf Entwicklungsprinzipien, die dazu dienen, Software wartbarer und skalierbarer zu machen. Die SOLID-Prinzipien sind:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"#srp\" data-type=\"internal\" data-id=\"#srp\">Single Responsibility Principle (SRP)<\/a> &#8211; eine Klasse sollte nur einen Grund haben, sich zu \u00e4ndern<\/li>\n\n\n\n<li><a href=\"#ocp\" data-type=\"URL\">Open-Closed-Prinzip (OCP)<\/a> &#8211; eine Klasse sollte offen f\u00fcr Erweiterungen, aber geschlossen f\u00fcr \u00c4nderungen sein<\/li>\n\n\n\n<li><a href=\"#lsp\" data-type=\"URL\">Liskov-Substitutionsprinzip (LSP)<\/a> &#8211; Subtypen sollten durch ihre Basistypen ersetzbar sein<\/li>\n\n\n\n<li><a href=\"#isp\" data-type=\"URL\">Interface Segregation Principle (ISP)<\/a> &#8211; viele spezifische Schnittstellen sind besser als eine allgemeine<\/li>\n\n\n\n<li><a href=\"#dip\" data-type=\"URL\">Dependency Inversion Principle (DIP)<\/a> &#8211; Abh\u00e4ngigkeit von Abstraktionen, nicht von Konkretionen<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Prinzipien wurden erstmals von <a href=\"https:\/\/twitter.com\/unclebobmartin\">Robert C. Martin<\/a> eingef\u00fchrt und sind in der objektorientierten Softwareentwicklung weit verbreitet, um einen flexibleren und wartbaren Code zu schreiben.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"srp\">Single Responsibility Principle (SRP)<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">&#8222;Eine Klasse sollte nur einen Grund haben, sich zu \u00e4ndern&#8220;<\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">class User {\n  private _username: string;\n  private _password: string;\n\n  constructor(username: string, password: string) {\n    this._username = username;\n    this._password = password;\n  }\n\n  public setUsername(username: string): void {\n    this._username = username;\n  }\n\n  public setPassword(password: string): void {\n    this._password = password;\n  }\n\n  public getUsername(): string {\n    return this._username;\n  }\n\n  public getPassword(): string {\n    return this._password;\n  }\n}\n\nclass UserValidator {\n  public isUsernameValid(username: string): boolean {\n    \/\/ implementation\n  }\n\n  public isPasswordValid(password: string): boolean {\n    \/\/ implementation\n  }\n}\n\nclass UserService {\n  public register(username: string, password: string): void {\n    const user = new User(username, password);\n    const validator = new UserValidator();\n\n    if (validator.isUsernameValid(user.getUsername()) &amp;&amp;\n        validator.isPasswordValid(user.getPassword())) {\n      \/\/ register user\n    } else {\n      \/\/ return error\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In diesem Beispiel hat die Klasse User die Aufgabe, Benutzerdaten zu speichern, und die Klasse UserValidator hat die Aufgabe, Benutzerdaten zu validieren. Die Klasse UserService hat die Aufgabe, einen Benutzer zu registrieren, und verwendet dazu die Klassen User und UserValidator.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beachten Sie, dass es sich hierbei um einfache Beispiele handelt und Sie bei der tats\u00e4chlichen Implementierung komplexere Szenarien und Randf\u00e4lle ber\u00fccksichtigen sollten. Au\u00dferdem k\u00f6nnen die SOLID-Grunds\u00e4tze auf verschiedenen Granularit\u00e4tsebenen angewandt werden, z. B. auf funktionaler Ebene oder auf Modulebene.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"ocp\">Open-Closed-Prinzip (OCP)<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">&#8222;Eine Klasse sollte offen f\u00fcr Erweiterungen, aber geschlossen f\u00fcr \u00c4nderungen sein&#8220;<\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">abstract class Shape {\n  abstract getArea(): number;\n}\n\nclass Circle extends Shape {\n  private _radius: number;\n\n  constructor(radius: number) {\n    super();\n    this._radius = radius;\n  }\n\n  getArea(): number {\n    return Math.PI * this._radius ** 2;\n  }\n}\n\nclass Rectangle extends Shape {\n  private _width: number;\n  private _height: number;\n\n  constructor(width: number, height: number) {\n    super();\n    this._width = width;\n    this._height = height;\n  }\n\n  getArea(): number {\n    return this._width * this._height;\n  }\n}\n\nclass AreaCalculator {\n  constructor(private _shapes: Shape[]) {}\n\n  sum(): number {\n    let area = 0;\n    this._shapes.forEach(shape =&gt; {\n      area += shape.getArea();\n    });\n    return area;\n  }\n}\n\nconst shapes = [new Circle(5), new Rectangle(5, 10)];\nconst calculator = new AreaCalculator(shapes);\nconsole.log(calculator.sum()); \/\/ prints 78.53981633974483<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In diesem Beispiel ist die Klasse <code>Shape<\/code> offen f\u00fcr Erweiterungen (Sie k\u00f6nnen neue Formen hinzuf\u00fcgen, indem Sie neue Klassen erstellen, die sie erweitern), aber geschlossen f\u00fcr \u00c4nderungen (Sie m\u00fcssen die Klasse <code>Shape<\/code> nicht \u00e4ndern, um neue Formen hinzuzuf\u00fcgen). Die Klasse <code>AreaCalculator<\/code> verwendet die Klasse <code>Shape<\/code> und ihre Unterklassen, um die Summe der Fl\u00e4chen der Shapes zu berechnen, und sie muss nicht ge\u00e4ndert werden, wenn Sie neue Shapes hinzuf\u00fcgen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So kann die Anwendung neue Shapes hinzuf\u00fcgen, ohne den bestehenden Code zu \u00e4ndern, und der <code>AreaCalculator<\/code> funktioniert auch mit den neuen Shapes ohne \u00c4nderung.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"lsp\">Liskov-Substitutionsprinzip (LSP)<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">&#8222;Subtypen sollten durch ihre Basistypen ersetzbar sein&#8220;<\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">class Bird {\n  fly(): void {\n    console.log(\"I am flying\");\n  }\n}\n\nclass Ostrich extends Bird {\n  fly(): void {\n    console.log(\"I can't fly but I can run very fast\");\n  }\n}\n\nfunction flyBird(bird: Bird) {\n  bird.fly();\n}\n\nconst bird = new Bird();\nconst ostrich = new Ostrich();\n\nflyBird(bird); \/\/ prints \"I am flying\"\nflyBird(ostrich); \/\/ prints \"I can't fly but I can run very fast\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In diesem Beispiel hat die Klasse Bird eine <code>fly<\/code>-Methode, und die Klasse <code>Ostrich<\/code> erweitert Bird, hat aber ihre eigene Implementierung der <code>fly<\/code>-Methode. Die Funktion <code>flyBird<\/code> nimmt ein <code>Bird<\/code> als Argument, aber wir k\u00f6nnen ihr auch ein <code>Ostrich<\/code>-Objekt \u00fcbergeben, da <code>Ostrich<\/code> eine Unterklasse von <code>Bird<\/code> ist und die gleichen Methoden implementiert, kann es die <code>Bird<\/code>-Klasse ersetzen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dies erm\u00f6glicht Flexibilit\u00e4t bei der Gestaltung der Klassen und der Funktionen, die sie verwenden. Das Liskov-Substitutionsprinzip besagt, dass Objekte einer Oberklasse durch Objekte einer Unterklasse ersetzt werden k\u00f6nnen sollten, ohne die Korrektheit des Programms zu beeintr\u00e4chtigen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In diesem Beispiel kann die Klasse <code>Ostrich<\/code> nicht fliegen, aber sie kann sehr schnell laufen, und sie ist ein g\u00fcltiger Ersatz f\u00fcr die Klasse <code>Bird<\/code>, man muss die Funktion <code>flyBird<\/code> nicht \u00e4ndern, um sie zu verwenden, und sie funktioniert trotzdem korrekt.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"isp\">Interface Segregation Principle (ISP)<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">&#8222;Viele spezifische Schnittstellen sind besser als eine allgemeine&#8220;<\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">interface Flyable {\n  fly(): void;\n}\n\ninterface Swimable {\n  swim(): void;\n}\n\ninterface Runnable {\n  run(): void;\n}\n\nclass Airplane implements Flyable {\n  fly(): void {\n    console.log(\"I am flying in the sky\");\n  }\n}\n\nclass Fish implements Swimable {\n  swim(): void {\n    console.log(\"I am swimming in the water\");\n  }\n}\n\nclass Human implements Runnable, Swimable {\n  run(): void {\n    console.log(\"I am running on the ground\");\n  }\n  swim(): void {\n    console.log(\"I am swimming in the water\");\n  }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In diesem Beispiel haben wir drei Schnittstellen: <code>Flyable<\/code>, <code>Swimable<\/code> und <code>Runnable<\/code>. Jede der Schnittstellen definiert ein einzelnes Verhalten. Die Klasse <code>Airplane<\/code> implementiert die Schnittstelle <code>Flyable<\/code> und kann fliegen, die Klasse <code>Fish<\/code> implementiert die Schnittstelle <code>Swimable<\/code> und kann schwimmen, und die Klasse <code>Human<\/code> implementiert die Schnittstellen <code>Runnable<\/code> und <code>Swimable<\/code> und kann laufen und schwimmen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Indem wir die Schnittstellen in kleinere, spezialisiertere Schnittstellen aufteilen, k\u00f6nnen wir Klassen erstellen, die nur die f\u00fcr sie relevanten Methoden implementieren m\u00fcssen. Dadurch wird der Code flexibler und leichter zu pflegen, da sich \u00c4nderungen an einer Schnittstelle nicht auf Klassen auswirken, die andere Schnittstellen implementieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Auf diese Weise k\u00f6nnen die Objekte nur die Methoden implementieren, die f\u00fcr sie relevant sind, und nicht die unn\u00f6tigen. Dies macht den Code flexibler und weniger anf\u00e4llig f\u00fcr \u00c4nderungen &#8211; das ist die Idee hinter dem Prinzip der Schnittstellentrennung.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"dip\">Dependency Inversion Principle (DIP)<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">&#8222;Abh\u00e4ngigkeit von Abstraktionen, nicht von Konkretionen&#8220;<\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">interface IWriter {\n  write(data: string): void;\n}\n\nclass FileWriter implements IWriter {\n  write(data: string): void {\n    \/\/ write data to a file\n  }\n}\n\nclass ConsoleWriter implements IWriter {\n  write(data: string): void {\n    console.log(data);\n  }\n}\n\nclass Logger {\n  private _writer: IWriter;\n\n  constructor(writer: IWriter) {\n    this._writer = writer;\n  }\n\n  log(data: string): void {\n    this._writer.write(data);\n  }\n}\n\nconst fileWriter = new FileWriter();\nconst consoleWriter = new ConsoleWriter();\nconst logger = new Logger(fileWriter);\nlogger.log(\"this will be written to a file\");\nlogger = new Logger(consoleWriter);\nlogger.log(\"this will be written to the console\");<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In diesem Beispiel h\u00e4ngt die <code>Logger<\/code>-Klasse von einer <code>IWriter<\/code>-Schnittstelle und nicht von einer konkreten Implementierung eines <code>Writers<\/code> ab. Durch die Abh\u00e4ngigkeit von einer Schnittstelle ist die <code>Logger<\/code>-Klasse nicht eng an eine bestimmte Implementierung eines <code>Writers<\/code> gekoppelt. Die Klassen <code>FileWriter<\/code> und <code>ConsoleWriter<\/code> implementieren die <code>IWriter<\/code>-Schnittstelle und k\u00f6nnen austauschbar mit der Klasse <code>Logger<\/code> verwendet werden. Dies erm\u00f6glicht eine gr\u00f6\u00dfere Flexibilit\u00e4t und eine einfachere \u00c4nderung des Codes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Prinzip der Abh\u00e4ngigkeitsinversion besagt, dass High-Level-Module nicht von Low-Level-Modulen abh\u00e4ngen sollten, sondern dass beide von Abstraktionen abh\u00e4ngen sollten. In diesem Beispiel h\u00e4ngt die Klasse Logger von der Schnittstelle IWriter ab, die eine Abstraktion ist, und nicht von konkreten Klassen wie <code>FileWriter<\/code> oder <code>ConsoleWriter<\/code>. Dadurch ist die Klasse <code>Logger<\/code> flexibler und leichter zu \u00e4ndern, da sie mit jeder Klasse arbeiten kann, die die IWriter-Schnittstelle implementiert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Auf diese Weise h\u00e4ngen die High-Level-Module von der Abstraktion (Schnittstellen) und nicht von spezifischen Low-Level-Modulen ab, was den Code flexibler und einfacher zu pflegen macht und auch weniger anf\u00e4llig f\u00fcr \u00c4nderungen.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>TypeScript hat bereits einen gro\u00dfen Einfluss darauf, dass man dem Clean-Code-Gedanken besser erf\u00fcllt. Doch man kann mit dem SOLID Prinzip [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":532,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[94],"tags":[97,96,95],"class_list":["post-529","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-patterns","tag-clean-code","tag-design-pattern","tag-solid"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/mlmwwmq2k3up.i.optimole.com\/w:auto\/h:auto\/q:mauto\/f:best\/https:\/\/i0.wp.com\/rising-bits.com\/wp-content\/uploads\/2023\/01\/solid-scaled.jpg?fit=2560%2C1131&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/posts\/529","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/comments?post=529"}],"version-history":[{"count":5,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/posts\/529\/revisions"}],"predecessor-version":[{"id":535,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/posts\/529\/revisions\/535"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/media\/532"}],"wp:attachment":[{"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/media?parent=529"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/categories?post=529"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rising-bits.com\/en\/wp-json\/wp\/v2\/tags?post=529"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}