Skip to content

Commit 7ae7dce

Browse files
committed
Add fluent interface design pattern
1 parent 922252b commit 7ae7dce

File tree

14 files changed

+232
-3
lines changed

14 files changed

+232
-3
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<h1 align="center">Design Patterns Library</h1>
22
<p align="center">
3-
32 Design Patterns • 65 moderately realistic examples
3+
33 Design Patterns • 73 moderately realistic examples
44
</p>
55

66
## What are Design Patterns?
@@ -57,6 +57,7 @@ This repository contains a comprehensive design patterns library implemented in
5757
| Design Pattern | Type | Description |
5858
| ------------- |:-------------:| -----|
5959
| [Event Aggregator](https://github.com/nemanjarogic/DesignPatternsLibrary/tree/main/src/AdditionalPatterns/EventAggregator/StoreManagement) | Behavioral | Channel events from multiple objects into a single object to simplify registration for clients.|
60+
| [Fluent Interface](https://github.com/nemanjarogic/DesignPatternsLibrary/tree/main/src/AdditionalPatterns/FluentInterface/FluentInterfaceLibrary) | Creational | Provides an easy-readable, flowing interface, that often mimics a domain specific language. Using this pattern results in code that can be read nearly as human language.|
6061
| [Interpreter](https://github.com/nemanjarogic/DesignPatternsLibrary/tree/main/src/AdditionalPatterns/Interpreter/InterpreterLibrary) | Behavioral | Defines a grammatical representation for a language and provides an interpreter to evaluate sentences in a language.|
6162
| [Lazy Load](https://github.com/nemanjarogic/DesignPatternsLibrary/tree/main/src/AdditionalPatterns/LazyLoad/LazyLoadLibrary) | Data Access | Defers initialization of an object until the point at which it is needed. It can contribute to efficiency in the program's operation if properly and appropriately used.|
6263
| [Null Object](https://github.com/nemanjarogic/DesignPatternsLibrary/tree/main/src/AdditionalPatterns/NullObject/NullObjectLibrary) | Behavioral | Encapsulates the absence of an object by providing a substitutable alternative that offers suitable default do nothing behavior.|

assets/images/console-menu.png

448 Bytes
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using BuildingBlocks;
2+
3+
namespace FluentInterfaceLibrary.BlobStorageExample;
4+
5+
public static class BlobStorageExecutor
6+
{
7+
public static void Execute()
8+
{
9+
ConsoleExtension.WriteSeparator("Blob Storage example");
10+
11+
BlobStorageManager
12+
.Connect("DefaultEndpointsProtocol=https;AccountName=myAccountName;AccountKey=<account-key>")
13+
.OnBlob("container", "blob")
14+
.Download("blobStorageManual.pdf")
15+
.ToFolder(@"D:\DesignPatternsLibrary\Downloads");
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using FluentInterfaceLibrary.BlobStorageExample.Contracts;
2+
3+
namespace FluentInterfaceLibrary.BlobStorageExample;
4+
5+
public sealed class BlobStorageManager : IBlobStorageSelector, IBlobStorageAction, IRead, IWrite
6+
{
7+
private readonly string _connectionString;
8+
private string _containerName;
9+
private string _blobName;
10+
11+
private BlobStorageManager(string connectionString)
12+
{
13+
_connectionString = connectionString;
14+
_containerName = string.Empty;
15+
_blobName = string.Empty;
16+
}
17+
18+
#region Blob storage connection
19+
20+
public static IBlobStorageSelector Connect(string connectionString)
21+
{
22+
Console.WriteLine($"Connecting to the storage account using the connection string: {connectionString}");
23+
var connection = new BlobStorageManager(connectionString);
24+
Console.WriteLine("Connection with the storage account is successfully established.");
25+
26+
return connection;
27+
}
28+
29+
public IBlobStorageAction OnBlob(string containerName, string blobName)
30+
{
31+
_containerName = containerName;
32+
_blobName = blobName;
33+
34+
Console.WriteLine($"The blob storage /{containerName}/{blobName} is ready for incoming requests.");
35+
return this;
36+
}
37+
38+
#endregion Blob storage connection
39+
40+
#region Download
41+
42+
public IWrite Download(string fileName)
43+
{
44+
Console.WriteLine($"The file {fileName} will be download from the /{_containerName}/{_blobName}");
45+
return this;
46+
}
47+
48+
public void ToFolder(string folderPath) =>
49+
Console.WriteLine($"The file is downloaded from the /{_containerName}/{_blobName} to the directory {folderPath}.");
50+
51+
#endregion Download
52+
53+
#region Upload
54+
55+
public IRead Upload(string fileName)
56+
{
57+
Console.WriteLine($"The file {fileName} will be uploaded to the /{_containerName}/{_blobName}");
58+
return this;
59+
}
60+
61+
public void FromFile(string filePath) =>
62+
Console.WriteLine($"The file is uploaded from the user's machine to the /{_containerName}/{_blobName}.");
63+
64+
public void FromStream(Stream stream) =>
65+
Console.WriteLine($"The file is uploaded from the stream to the /{_containerName}/{_blobName}.");
66+
67+
#endregion Upload
68+
69+
#region Preview
70+
71+
public void Preview(string fileName)
72+
{
73+
Console.WriteLine($"Previewing the file {fileName} from the /{_containerName}/{_blobName}...");
74+
}
75+
76+
#endregion Preview
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace FluentInterfaceLibrary.BlobStorageExample.Contracts;
2+
3+
// Interfaces IWrite and IRead are used as a prevention mechanism for invalid method combinations.
4+
public interface IBlobStorageAction
5+
{
6+
IWrite Download(string fileName);
7+
IRead Upload(string fileName);
8+
void Preview(string fileName);
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace FluentInterfaceLibrary.BlobStorageExample.Contracts;
2+
3+
public interface IBlobStorageSelector
4+
{
5+
IBlobStorageAction OnBlob(string containerName, string blobName);
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace FluentInterfaceLibrary.BlobStorageExample.Contracts;
2+
3+
public interface IRead
4+
{
5+
void FromFile(string filePath);
6+
void FromStream(Stream stream);
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace FluentInterfaceLibrary.BlobStorageExample.Contracts;
2+
3+
public interface IWrite
4+
{
5+
void ToFolder(string folderPath);
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using BuildingBlocks;
2+
using FluentInterfaceLibrary.BlobStorageExample;
3+
using FluentInterfaceLibrary.LinqExample;
4+
5+
namespace FluentInterfaceLibrary;
6+
7+
public class Executor : PatternExecutor
8+
{
9+
public override string Name => "Fluent Interface - Creational Pattern";
10+
11+
public override void Execute()
12+
{
13+
BlobStorageExecutor.Execute();
14+
LinqExecutor.Execute();
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\..\..\BuildingBlocks\BuildingBlocks.csproj" />
11+
</ItemGroup>
12+
13+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using BuildingBlocks;
2+
3+
namespace FluentInterfaceLibrary.LinqExample;
4+
5+
public static class LinqExecutor
6+
{
7+
public static void Execute()
8+
{
9+
ConsoleExtension.WriteSeparator("LINQ example");
10+
11+
var englishToSerbianDictionary = new Dictionary<string, string>
12+
{
13+
{"adventure", "avantura"},
14+
{"bird", "ptica"},
15+
{"fish", "riba"},
16+
{"football", "fudbal"},
17+
{"programming", "programiranje"},
18+
};
19+
20+
DisplayDictionary(englishToSerbianDictionary);
21+
22+
Console.WriteLine("\nFinding translations for English words containing the letter 'a', sorted by length and displayed in uppercase...");
23+
FindTranslationsProgressively(englishToSerbianDictionary);
24+
FindTranslationsUsingFluentInterface(englishToSerbianDictionary);
25+
}
26+
27+
private static void DisplayDictionary(Dictionary<string, string> englishToSerbianDictionary)
28+
{
29+
Console.WriteLine("\nContent of the dictionary: ");
30+
foreach (var (englishWord, serbianWord) in englishToSerbianDictionary)
31+
{
32+
Console.WriteLine($"{englishWord} - {serbianWord}");
33+
}
34+
}
35+
36+
private static void FindTranslationsProgressively(Dictionary<string, string> englishToSerbianDictionary)
37+
{
38+
var filtered = englishToSerbianDictionary.Where(t => t.Key.Contains("a"));
39+
var sorted = filtered.OrderBy(t => t.Value.Length);
40+
var finalTranslations = sorted.Select(t => t.Value.ToUpper());
41+
42+
Console.WriteLine("\nProgressive translations: ");
43+
DisplayWords(finalTranslations);
44+
}
45+
46+
private static void FindTranslationsUsingFluentInterface(Dictionary<string, string> englishToSerbianDictionary)
47+
{
48+
// C# uses fluent programming extensively in LINQ to build queries using "standard query operators".
49+
var finalTranslations = englishToSerbianDictionary
50+
.Where(t => t.Key.Contains("a"))
51+
.OrderBy(t => t.Value.Length)
52+
.Select(t => t.Value.ToUpper());
53+
54+
Console.WriteLine("\nFluent interface translations: ");
55+
DisplayWords(finalTranslations);
56+
}
57+
58+
private static void DisplayWords(IEnumerable<string> words)
59+
{
60+
foreach (var word in words)
61+
{
62+
Console.WriteLine(word);
63+
}
64+
}
65+
}

src/DesignPatternsLibrary.sln

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 16
4-
VisualStudioVersion = 16.0.30711.63
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.2.32616.157
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DesignPatternsLibrary", "DesignPatternsLibrary\DesignPatternsLibrary.csproj", "{60C3B7E4-E290-47F4-A3C0-4F9505DE926F}"
77
EndProject
@@ -141,6 +141,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specification", "Specificat
141141
EndProject
142142
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProductSpecification", "AdditionalPatterns\Specification\ProductSpecification\ProductSpecification.csproj", "{82AC53EE-D473-4826-9443-133BED0C830C}"
143143
EndProject
144+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FluentInterface", "FluentInterface", "{6CBEFB1A-4A1D-4F1E-8878-C6AE19444596}"
145+
EndProject
146+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentInterfaceLibrary", "AdditionalPatterns\FluentInterface\FluentInterfaceLibrary\FluentInterfaceLibrary.csproj", "{97D64ED5-9F0A-437A-A96C-D338D0CAE390}"
147+
EndProject
144148
Global
145149
GlobalSection(SolutionConfigurationPlatforms) = preSolution
146150
Debug|Any CPU = Debug|Any CPU
@@ -283,6 +287,10 @@ Global
283287
{82AC53EE-D473-4826-9443-133BED0C830C}.Debug|Any CPU.Build.0 = Debug|Any CPU
284288
{82AC53EE-D473-4826-9443-133BED0C830C}.Release|Any CPU.ActiveCfg = Release|Any CPU
285289
{82AC53EE-D473-4826-9443-133BED0C830C}.Release|Any CPU.Build.0 = Release|Any CPU
290+
{97D64ED5-9F0A-437A-A96C-D338D0CAE390}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
291+
{97D64ED5-9F0A-437A-A96C-D338D0CAE390}.Debug|Any CPU.Build.0 = Debug|Any CPU
292+
{97D64ED5-9F0A-437A-A96C-D338D0CAE390}.Release|Any CPU.ActiveCfg = Release|Any CPU
293+
{97D64ED5-9F0A-437A-A96C-D338D0CAE390}.Release|Any CPU.Build.0 = Release|Any CPU
286294
EndGlobalSection
287295
GlobalSection(SolutionProperties) = preSolution
288296
HideSolutionNode = FALSE
@@ -351,6 +359,8 @@ Global
351359
{BBDF24BC-7D73-4A8F-B8B0-C649019E1B80} = {C8512694-34BF-4616-A82F-12D73DE8263A}
352360
{F1704AAE-3951-43C1-85EC-16C4C52133F3} = {15B15698-CB27-4F30-884C-25C2C1D50202}
353361
{82AC53EE-D473-4826-9443-133BED0C830C} = {F1704AAE-3951-43C1-85EC-16C4C52133F3}
362+
{6CBEFB1A-4A1D-4F1E-8878-C6AE19444596} = {15B15698-CB27-4F30-884C-25C2C1D50202}
363+
{97D64ED5-9F0A-437A-A96C-D338D0CAE390} = {6CBEFB1A-4A1D-4F1E-8878-C6AE19444596}
354364
EndGlobalSection
355365
GlobalSection(ExtensibilityGlobals) = postSolution
356366
SolutionGuid = {F9238CD9-9068-4B63-8D9E-53684BA60A21}

src/DesignPatternsLibrary/DesignPatternsLibrary.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
<ItemGroup>
1111
<ProjectReference Include="..\AdditionalPatterns\EventAggregator\StoreManagement\StoreManagement.csproj" />
12+
<ProjectReference Include="..\AdditionalPatterns\FluentInterface\FluentInterfaceLibrary\FluentInterfaceLibrary.csproj" />
1213
<ProjectReference Include="..\AdditionalPatterns\Interpreter\InterpreterLibrary\InterpreterLibrary.csproj" />
1314
<ProjectReference Include="..\AdditionalPatterns\LazyLoad\LazyLoadLibrary\LazyLoadLibrary.csproj" />
1415
<ProjectReference Include="..\AdditionalPatterns\NullObject\NullObjectLibrary\NullObjectLibrary.csproj" />

src/DesignPatternsLibrary/PatternExecutorsRegistry.cs

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ private PatternExecutorsRegistry()
2323
new CompositeLibrary.Executor(),
2424
new DecoratorLibrary.Executor(),
2525
new FacadeLibrary.Executor(),
26+
new FluentInterfaceLibrary.Executor(),
2627
new FlyweightLibrary.Executor(),
2728
new ProxyLibrary.Executor(),
2829
new ChainOfResponsibilityLibrary.Executor(),

0 commit comments

Comments
 (0)