Skip to content

Commit 4f8ca8c

Browse files
authored
feat: Add ThreadSafeArray and ThreadSafeDictionary (#5)
* Feat: Add missing design patterns (Pipeline, Composite, CoR, Command, Iterator, Decorator) * Feat: Add ThreadSafe and Registry DAK patterns * feat: Add ThreadSafeArray and ThreadSafeDictionary Implements thread-safe wrappers for standard Swift collection types using the ThreadSafe<T> pattern. * feat: Add tests for ThreadSafeArray, ThreadSafeDictionary, and Registry * test: Increase code coverage for behavioral and structural patterns * test: Add comprehensive unit tests for all DAK patterns
1 parent ed038f0 commit 4f8ca8c

20 files changed

Lines changed: 1879 additions & 0 deletions
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//
2+
// ChainOfResponsibility.swift
3+
// DesignAlgorithmsKit
4+
//
5+
// Chain of Responsibility Pattern - Pass requests along a chain of handlers
6+
//
7+
8+
import Foundation
9+
10+
/// Protocol for a handler in the chain
11+
public protocol Handler: AnyObject {
12+
/// The next handler in the chain
13+
var nextHandler: Handler? { get set }
14+
15+
/// Handle a request
16+
/// - Parameter request: The request to handle
17+
/// - Returns: Result if handled, nil otherwise
18+
func handle(_ request: Any) -> Any?
19+
}
20+
21+
/// Base implementation of a handler
22+
open class BaseHandler: Handler {
23+
public var nextHandler: Handler?
24+
25+
public init(next: Handler? = nil) {
26+
self.nextHandler = next
27+
}
28+
29+
/// Set the next handler in the chain
30+
/// - Parameter handler: The next handler
31+
/// - Returns: The handler that was set (for chaining)
32+
@discardableResult
33+
public func setNext(_ handler: Handler) -> Handler {
34+
self.nextHandler = handler
35+
return handler
36+
}
37+
38+
open func handle(_ request: Any) -> Any? {
39+
if let next = nextHandler {
40+
return next.handle(request)
41+
}
42+
return nil
43+
}
44+
}
45+
46+
/// A type-safe version of the Chain of Responsibility
47+
public protocol TypedHandler: AnyObject {
48+
associatedtype Request
49+
associatedtype Response
50+
51+
var nextHandler: (any TypedHandler)? { get set }
52+
53+
func handle(_ request: Request) -> Response?
54+
}
55+
56+
/// Base implementation for typed handlers
57+
open class BaseTypedHandler<T, R>: TypedHandler {
58+
public typealias Request = T
59+
public typealias Response = R
60+
61+
// We use a type-erased wrapper or force cast internally because generic protocols as types are tricky
62+
// For simplicity in this generic pattern library, we'll store specific typed handler
63+
public var nextTypedHandler: BaseTypedHandler<T, R>?
64+
65+
// Conformance to protocol (computed property due to associatedtype limits)
66+
public var nextHandler: (any TypedHandler)? {
67+
get { return nextTypedHandler }
68+
set { nextTypedHandler = newValue as? BaseTypedHandler<T, R> }
69+
}
70+
71+
public init() {}
72+
73+
@discardableResult
74+
public func setNext(_ handler: BaseTypedHandler<T, R>) -> BaseTypedHandler<T, R> {
75+
self.nextTypedHandler = handler
76+
return handler
77+
}
78+
79+
open func handle(_ request: T) -> R? {
80+
return nextTypedHandler?.handle(request)
81+
}
82+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// Command.swift
3+
// DesignAlgorithmsKit
4+
//
5+
// Command Pattern - Encapsulate a request as an object
6+
//
7+
8+
import Foundation
9+
10+
/// Protocol for commands
11+
public protocol Command {
12+
/// Execute the command
13+
func execute()
14+
15+
/// Undo the command (optional)
16+
func undo()
17+
}
18+
19+
/// Base command implementation
20+
open class BaseCommand: Command {
21+
public init() {}
22+
23+
open func execute() {
24+
// To be implemented by subclasses
25+
}
26+
27+
open func undo() {
28+
// To be implemented by subclasses
29+
}
30+
}
31+
32+
/// A command that wraps a simple closure
33+
public class ClosureCommand: Command {
34+
private let action: () -> Void
35+
private let undoAction: (() -> Void)?
36+
37+
public init(action: @escaping () -> Void, undoAction: (() -> Void)? = nil) {
38+
self.action = action
39+
self.undoAction = undoAction
40+
}
41+
42+
public func execute() {
43+
action()
44+
}
45+
46+
public func undo() {
47+
undoAction?()
48+
}
49+
}
50+
51+
/// Invoker responsible for executing commands
52+
open class CommandInvoker {
53+
private var history: [Command] = []
54+
private var undoStack: [Command] = []
55+
56+
public init() {}
57+
58+
/// Execute a command
59+
public func execute(_ command: Command) {
60+
command.execute()
61+
history.append(command)
62+
undoStack.removeAll() // Clear redo stack on new operation
63+
}
64+
65+
/// Undo the last command
66+
public func undo() {
67+
guard let command = history.popLast() else { return }
68+
command.undo()
69+
undoStack.append(command)
70+
}
71+
72+
/// Redo the last undone command
73+
public func redo() {
74+
guard let command = undoStack.popLast() else { return }
75+
command.execute()
76+
history.append(command)
77+
}
78+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// Iterator.swift
3+
// DesignAlgorithmsKit
4+
//
5+
// Iterator Pattern - Access elements of a collection consistently
6+
//
7+
8+
import Foundation
9+
10+
/// Protocol for iterators
11+
public protocol Iterator {
12+
associatedtype Element
13+
14+
/// Check if there are more elements
15+
func hasNext() -> Bool
16+
17+
/// Get the next element
18+
func next() -> Element?
19+
}
20+
21+
/// Protocol for iterable aggregates
22+
public protocol Iterable {
23+
associatedtype IteratorType: Iterator
24+
25+
/// Create an iterator
26+
func makeIterator() -> IteratorType
27+
}
28+
29+
/// A concrete iterator for array-based collections
30+
public class ArrayIterator<T>: Iterator {
31+
private let items: [T]
32+
private var currentIndex = 0
33+
34+
public init(_ items: [T]) {
35+
self.items = items
36+
}
37+
38+
public func hasNext() -> Bool {
39+
return currentIndex < items.count
40+
}
41+
42+
public func next() -> T? {
43+
guard hasNext() else { return nil }
44+
let item = items[currentIndex]
45+
currentIndex += 1
46+
return item
47+
}
48+
}
49+
50+
/// A concrete iterator for tree structures (depth-first)
51+
public class TreeIterator<T>: Iterator {
52+
private var stack: [T] = []
53+
private let getChildren: (T) -> [T]
54+
55+
public init(root: T, getChildren: @escaping (T) -> [T]) {
56+
self.stack = [root]
57+
self.getChildren = getChildren
58+
}
59+
60+
public func hasNext() -> Bool {
61+
return !stack.isEmpty
62+
}
63+
64+
public func next() -> T? {
65+
guard !stack.isEmpty else { return nil }
66+
let current = stack.removeLast()
67+
// Add children to stack in reverse order to process them in original order
68+
let children = getChildren(current)
69+
for child in children.reversed() {
70+
stack.append(child)
71+
}
72+
return current
73+
}
74+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//
2+
// Pipeline.swift
3+
// DesignAlgorithmsKit
4+
//
5+
// Pipeline Pattern - Process data through a sequence of stages
6+
//
7+
8+
import Foundation
9+
10+
/// Protocol for a pipeline stage
11+
public protocol DataPipelineStage {
12+
/// The input type for this stage
13+
associatedtype Input
14+
15+
/// The output type for this stage
16+
associatedtype Output
17+
18+
/// Process the input and produce output
19+
/// - Parameter input: Input data
20+
/// - Returns: Processed output
21+
/// - Throws: Error if processing fails
22+
func process(_ input: Input) throws -> Output
23+
}
24+
25+
/// Protocol for an asynchronous pipeline stage
26+
public protocol AsyncDataPipelineStage {
27+
/// The input type for this stage
28+
associatedtype Input
29+
30+
/// The output type for this stage
31+
associatedtype Output
32+
33+
/// Process the input asynchronously
34+
/// - Parameter input: Input data
35+
/// - Returns: Processed output
36+
/// - Throws: Error if processing fails
37+
func process(_ input: Input) async throws -> Output
38+
}
39+
40+
/// A pipeline that executes stages sequentially
41+
///
42+
/// The pipeline pattern allows processing data through a sequence of stages,
43+
/// where the output of one stage becomes the input of the next.
44+
open class DataPipeline<Input, Output> {
45+
private let operation: (Input) throws -> Output
46+
47+
/// Initialize with a processing function
48+
public init(_ operation: @escaping (Input) throws -> Output) {
49+
self.operation = operation
50+
}
51+
52+
/// Execute the pipeline
53+
public func execute(_ input: Input) throws -> Output {
54+
return try operation(input)
55+
}
56+
57+
/// Append a new stage to the pipeline
58+
public func appending<S: DataPipelineStage>(_ stage: S) -> DataPipeline<Input, S.Output> where S.Input == Output {
59+
return DataPipeline<Input, S.Output> { input in
60+
let intermediate = try self.execute(input)
61+
return try stage.process(intermediate)
62+
}
63+
}
64+
65+
/// Append a closure stage
66+
public func appending<NewOutput>(_ closure: @escaping (Output) throws -> NewOutput) -> DataPipeline<Input, NewOutput> {
67+
return DataPipeline<Input, NewOutput> { input in
68+
let intermediate = try self.execute(input)
69+
return try closure(intermediate)
70+
}
71+
}
72+
}
73+
74+
/// An asynchronous pipeline
75+
open class AsyncDataPipeline<Input, Output> {
76+
private let operation: (Input) async throws -> Output
77+
78+
public init(_ operation: @escaping (Input) async throws -> Output) {
79+
self.operation = operation
80+
}
81+
82+
public func execute(_ input: Input) async throws -> Output {
83+
return try await operation(input)
84+
}
85+
86+
public func appending<S: AsyncDataPipelineStage>(_ stage: S) -> AsyncDataPipeline<Input, S.Output> where S.Input == Output {
87+
return AsyncDataPipeline<Input, S.Output> { input in
88+
let intermediate = try await self.execute(input)
89+
return try await stage.process(intermediate)
90+
}
91+
}
92+
93+
public func appending<NewOutput>(_ closure: @escaping (Output) async throws -> NewOutput) -> AsyncDataPipeline<Input, NewOutput> {
94+
return AsyncDataPipeline<Input, NewOutput> { input in
95+
let intermediate = try await self.execute(input)
96+
return try await closure(intermediate)
97+
}
98+
}
99+
}

0 commit comments

Comments
 (0)