🚀 The ultimate collection of Software Design Patterns & Principles with examples [38/79 Patterns].
🚀 The ultimate collection of various Software Design Patterns
implemented using Swift Programming Language
. This repository aims to collect as many design patterns as possbile with examples and to serve as a reference material for everyone who is willing to learn something new.
Each design pattern has a complete description and source code. You can view the description by clicking the name of a design pattern and view the code by clicking the [code].
The primary objective of this project is to assist others in learning by compiling and sharing the knowledge I have acquired. Given the vast amount of material to be documented, there may be instances when my thoughts are expressed in a manner that appears peculiar or unclear. I have included reference materials to enable you to examine similar patterns from different perspectives or original sources.
If you come across any unclear or problematic content, please refrain from forming an immediate negative judgment. As this is an open-source project, you have the opportunity to politely inform me of any issues or even submit a pull request, which is the most effective way to contribute. Another motivating factor for this project is my desire to create learning materials that are both ‘solid’ and ‘easy’ to comprehend for developers who are familiar with the basics of OOP and POP paradigms, as these are crucial for working with Swift.
Nonetheless, in some contexts and literature certain terminology is not reffered as design pattern (for example Weak Reference). My intention here is to provide explanations and use cases without initiating any heated debates.
Behavioral design patterns are design patterns that identify common communication patterns between objects and realize these patterns. By doing so, these patterns increase flexibility in carrying out this communication.
Source: wikipedia.org
Publish/Subscribe
and Multicast Delegate
patterns [code]Observer
, Event Listener
and Multicast Delegate
, however it has its differences and weaknesses as wellCreational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or in added complexity to the design. Creational design patterns solve this problem by somehow controlling this object creation.
Source: wikipedia.org
simple factory
because it centralizes the object creation process, making it easier to manage and maintain.Structural design patterns are design patterns that ease the design by identifying a simple way to realize relationships between entities.
Source: wikipedia.org
Delegate pattern
which allows multiple delegates to be notified about method calls [code]DAO
stands for Data Access Object
. DAO
provides abstract interface to a database or other persistence storage mechanismFramework
- hide complex code behind a simple interfaceConcurrency patterns are those types of design patterns that deal with the multi-threaded programming paradigm.
Source: wikipedia.org
Architectural patterns addres issues that arise in software engineering within certain contexts. They are similar to design patterns, but have broader scope of responsibilities. Architectural patterns structure the way how software product/application/platform is built by separating different abstractions into reusable layers. Sometimes architectural patterns are documented as design patterns.
MVC
design pattern decouples these major components allowing for efficient code reuse and parallel development [1]MVC
) architectural pattern, and is used mostly for building user interfaces. In MVP
, the presenter assumes the functionality of the “middle-man”. In MVP
, all presentation logic is pushed to the presenter [1]GUI
code – from development of the business logic or back-end logic (the data model). The view model of MVVM
is a value converter, meaning the view model is responsible for exposing (converting) the data objects from the model in such a way that objects are easily managed and presented [1]MVVM
arcitectural pattern, but with an addition of Routing
layer, which is Coordinator
patternVIPER
is an application of Clean Architecture
to iOS
apps. The word VIPER
is a backronym for View
, Interactor
, Presenter
, Entity
, and Routing
. Clean Architecture
divides an app’s logical structure into distinct layers of responsibility. This makes it easier to isolate dependencies (e.g. your database) and to test the interactions at the boundaries between layers [8]Clean Archtiecure
proposed by Uncle BobIn
Swift
, there are two basic kinds of patterns: those that successfully match any kind of value, and those that may fail to match a specified value at runtime.The first kind of pattern is used for destructuring values in simple variable, constant, and optional bindings. These include wildcard patterns, identifier patterns, and any value binding or tuple patterns containing them. You can specify a type annotation for these patterns to constrain them to match only values of a certain type.
The second kind of pattern is used for full pattern matching, where the values you’re trying to match against may not be there at runtime. These include enumeration case patterns, optional patterns, expression patterns, and type-casting patterns. You use these patterns in a case label of a switch statement, a catch clause of a do statement, or in the case condition of an if, while, guard, or for-in statement.
Source:
swift.org
The following patterns are not part of the design patterns
topic. However, the presented language patterns
are related to the design patterns
theme, since they offer reusable solutions for common design scenarious when using the programming language. IMHO
they are essential to master in order to realise that patterns are everywhere - language, constructions, architectures, approaches etc.
if
, while
, guard
, and for-in
statementssome(Wrapped)
case of an Optional<Wrapped>
enumeration. Optional patterns consist of an identifier pattern followed immediately by a question mark and appear in the same places as enumeration case patternsswitch
statement case
labelsThere are numerous software design principles that help to create maintainable, scalable, and efficient software systems. These principles, along with other best practices, can guide developers in creating high-quality software systems that are easy to understand, maintain, and extend.
SOLID
Principles:Single Responsibility
Principle (SRP): A class should have only one reason to change, which means it should have only one responsibility.Open/Closed
Principle (OCP): Software entities should be open for extension but closed for modification.Liskov Substitution
Principle (LSP): Subtypes should be substitutable for their base types without altering the correctness of the program.Interface Segregation
Principle (ISP): Clients should not be forced to implement interfaces they don’t use; instead, create specific interfaces for each client.Dependency Inversion
Principle (DIP): High-level modules should not depend on low-level modules; both should depend on abstractions. Additionally, abstractions should not depend on details; details should depend on abstractions.DRY
(Don’t Repeat Yourself): Avoid duplicating code and functionality; aim for modularity and reusability.KISS
(Keep It Simple, Stupid): Design should be as simple as possible, and complexity should be avoided whenever possible.YAGNI
(You Aren’t Gonna Need It): Do not implement features or functionality until they are actually needed, as it can lead to unnecessary complexity.Separation of Concerns
(SoC): Divide your application into distinct sections, each with a specific responsibility, to improve maintainability and modularity.Encapsulation
: Bundle data and the methods that operate on that data within a single unit, thereby hiding the internal state and implementation details from other parts of the system.Composition over Inheritance
: Favor object composition over class inheritance to promote flexibility and avoid issues related to deep inheritance hierarchies.Law of Demeter
(LoD) / Principle of Least Knowledge: An object should only communicate with its immediate neighbors and should not have knowledge of the inner workings of other objects.Principle of Proximity
: Related code and functionality should be placed close together, promoting cohesion and making it easier to understand the relationships between components.Fail-Fast
: Design the system to fail as soon as possible when something goes wrong, making it easier to identify and fix problems.GRASP
(General Responsibility Assignment Software Patterns):Information Expert
: Assign a responsibility to the class that has the necessary information.Creator
: Assign the responsibility of creating an object to the class that uses it the most or has the necessary information to create it.Controller
: Assign the responsibility of handling external events to a specific class, often referred to as a controller.Low Coupling
: Minimize dependencies between classes to promote modularity and ease of maintenance.High Cohesion
: Keep related responsibilities within the same class, making the class easier to understand and maintain.Polymorphism
: Use inheritance and interfaces to create flexible and reusable solutions.Pure Fabrication
: Create artificial classes to achieve low coupling and high cohesion when necessary.Indirection
: Introduce an intermediate class to manage relationships between other classes, reducing coupling.Protected Variations
: Identify points of potential variation and create stable interfaces around them to minimize the impact of changes.Convention Over Configuration
: Establish sensible defaults and conventions to minimize the amount of configuration required, making it easier for developers to work with the system.Code Reuse
: Reuse existing code, libraries, or frameworks instead of reinventing the wheel. This saves time and effort and often results in more stable and efficient solutions.Incremental Development
: Develop and deliver software in small, manageable increments, allowing for rapid feedback and adjustment.Test-Driven Development
(TDD): Write tests before writing the actual code, ensuring that code meets the desired specifications and improving the overall quality of the software.Continuous Integration
(CI) and Continuous Delivery (CD): Integrate code changes frequently and automatically, and deploy updates to production with minimal human intervention. This promotes rapid feedback and reduces the risk of introducing errors.Principle of Least Astonishment
: Design the software so that its behavior is predictable and intuitive, minimizing surprises for users and developers.Scalability
: Design the system to handle increased workload efficiently by adding more resources or optimizing resource utilization.Modularity
: Break down a complex system into smaller, manageable modules that can be developed, tested, and maintained independently.Maintainability
: Design the software to be easy to understand, modify, and extend, allowing for efficient long-term maintenance.Readability
: Write code that is easy to read and understand, which makes it easier for others (and yourself) to maintain and modify the software.Self-Documenting Code
: Write code that clearly communicates its intent, reducing the need for separate documentation and making it easier to maintain.Loose Coupling
: Strive to minimize dependencies between components, allowing them to evolve independently and reducing the risk of cascading changes.Orthogonality
: Design components to have a single, well-defined responsibility and ensure that their behavior is independent of other components. This makes the system more modular, maintainable, and less error-prone.Principle of Least Privilege
: Give components the minimum level of access and authority required to perform their tasks, reducing the potential impact of security breaches or bugs.Defensive Programming
: Write code that anticipates and handles potential errors and exceptional situations, ensuring that the system remains stable and secure even in unexpected circumstances.Design by Contract
: Clearly define the responsibilities and expectations of components (preconditions, postconditions, and invariants), improving the overall robustness and reliability of the system.Feature Toggle
: Use feature flags or toggles to enable or disable features in the software, allowing for easier management of experimental features and phased rollouts.Continuous Improvement
: Regularly review and refine the codebase, adopting new best practices and technologies as they emerge, and addressing technical debt to keep the system maintainable and efficient.Domain-Driven Design
(DDD): Focus on the core domain and its logic, using a common language (ubiquitous language) among developers and domain experts, and creating a model that accurately represents the domain.Separation of Interface and Implementation
: Keep the interface of a component separate from its implementation, allowing for flexibility and interchangeability without affecting the rest of the system.Code Consistency
: Establish and follow coding standards and conventions across the entire codebase, making it easier to read, understand, and maintain.Performance Optimization
: Design the software with performance in mind, using appropriate data structures, algorithms, and techniques to minimize resource usage and response times.Flexibility and Adaptability
: Design the system to be easily modified and extended to accommodate changing requirements and new features.Security by Design
: Integrate security best practices and considerations into every stage of the software development process, minimizing the risk of vulnerabilities and breaches.Cost-Effective Development
: Strive to balance the trade-offs between development time, code quality, and performance, aiming for a cost-effective approach that meets the project requirements.Design Patterns
: Leverage proven design patterns that solve common problems, making the code more reusable, modular, and maintainable.Antifragility
: Design systems that not only tolerate failures and stress but also improve and adapt when faced with challenges.Microservices Architecture
: Decompose a monolithic system into smaller, loosely-coupled services, each with a single responsibility, which can be developed, deployed, and scaled independently.Resource Management
: Ensure efficient allocation, use, and release of resources (e.g., memory, file handles, sockets) to prevent leaks and other performance issues.Cross-Platform Compatibility
: Design the software to work across multiple platforms and environments, ensuring a consistent user experience and broad accessibility.Versioning
: Implement version control for both code and APIs, enabling better collaboration, easier rollback of changes, and compatibility between different versions of the software.Design for Testability
: Develop the software in a way that facilitates testing at various levels (unit, integration, system), ensuring that issues can be identified and addressed early in the development process.Monitoring and Observability
: Build monitoring and observability features into the system, allowing for easy identification and diagnosis of issues in production environments.Design for Accessibility
: Consider the needs of users with disabilities, ensuring that the software is usable by as many people as possible.Localization and Internationalization
: Design the software to support multiple languages, currencies, and cultural conventions, making it easier to adapt the product for different regions.Graceful Degradation
: Ensure that the software continues to function, albeit with reduced capabilities, in the face of partial failures or adverse conditions.Progressive Enhancement
: Start with a basic, functional version of the software and incrementally add enhancements, ensuring that the application remains usable and accessible even on older or less-capable devices.Cache Management
: Use caching techniques to store and quickly retrieve frequently used data, improving performance and reducing the load on external systems.Responsiveness
: Design the software to provide quick and timely feedback to user actions, ensuring a smooth and satisfying user experience.There were used a number of reference materials such as:
Your contributions to this project are warmly welcomed! To ensure a smooth collaboration, please follow these guidelines:
.playground
file. This way, code compilation is as simple as launching the file, and there’s no need to manage a large Xcode
project.The project is availabe under MIT licence.