Skip to content

Commit

Permalink
Add fluent interface design pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
nemanjarogic committed Sep 10, 2022
1 parent 922252b commit 7ae7dce
Show file tree
Hide file tree
Showing 14 changed files with 232 additions and 3 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<h1 align="center">Design Patterns Library</h1>
<p align="center">
32 Design Patterns • 65 moderately realistic examples
33 Design Patterns • 73 moderately realistic examples
</p>

## What are Design Patterns?
Expand Down Expand Up @@ -57,6 +57,7 @@ This repository contains a comprehensive design patterns library implemented in
| Design Pattern | Type | Description |
| ------------- |:-------------:| -----|
| [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.|
| [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.|
| [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.|
| [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.|
| [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.|
Expand Down
Binary file modified assets/images/console-menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using BuildingBlocks;

namespace FluentInterfaceLibrary.BlobStorageExample;

public static class BlobStorageExecutor
{
public static void Execute()
{
ConsoleExtension.WriteSeparator("Blob Storage example");

BlobStorageManager
.Connect("DefaultEndpointsProtocol=https;AccountName=myAccountName;AccountKey=<account-key>")
.OnBlob("container", "blob")
.Download("blobStorageManual.pdf")
.ToFolder(@"D:\DesignPatternsLibrary\Downloads");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using FluentInterfaceLibrary.BlobStorageExample.Contracts;

namespace FluentInterfaceLibrary.BlobStorageExample;

public sealed class BlobStorageManager : IBlobStorageSelector, IBlobStorageAction, IRead, IWrite
{
private readonly string _connectionString;
private string _containerName;
private string _blobName;

private BlobStorageManager(string connectionString)
{
_connectionString = connectionString;
_containerName = string.Empty;
_blobName = string.Empty;
}

#region Blob storage connection

public static IBlobStorageSelector Connect(string connectionString)
{
Console.WriteLine($"Connecting to the storage account using the connection string: {connectionString}");
var connection = new BlobStorageManager(connectionString);
Console.WriteLine("Connection with the storage account is successfully established.");

return connection;
}

public IBlobStorageAction OnBlob(string containerName, string blobName)
{
_containerName = containerName;
_blobName = blobName;

Console.WriteLine($"The blob storage /{containerName}/{blobName} is ready for incoming requests.");
return this;
}

#endregion Blob storage connection

#region Download

public IWrite Download(string fileName)
{
Console.WriteLine($"The file {fileName} will be download from the /{_containerName}/{_blobName}");
return this;
}

public void ToFolder(string folderPath) =>
Console.WriteLine($"The file is downloaded from the /{_containerName}/{_blobName} to the directory {folderPath}.");

#endregion Download

#region Upload

public IRead Upload(string fileName)
{
Console.WriteLine($"The file {fileName} will be uploaded to the /{_containerName}/{_blobName}");
return this;
}

public void FromFile(string filePath) =>
Console.WriteLine($"The file is uploaded from the user's machine to the /{_containerName}/{_blobName}.");

public void FromStream(Stream stream) =>
Console.WriteLine($"The file is uploaded from the stream to the /{_containerName}/{_blobName}.");

#endregion Upload

#region Preview

public void Preview(string fileName)
{
Console.WriteLine($"Previewing the file {fileName} from the /{_containerName}/{_blobName}...");
}

#endregion Preview
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace FluentInterfaceLibrary.BlobStorageExample.Contracts;

// Interfaces IWrite and IRead are used as a prevention mechanism for invalid method combinations.
public interface IBlobStorageAction
{
IWrite Download(string fileName);
IRead Upload(string fileName);
void Preview(string fileName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace FluentInterfaceLibrary.BlobStorageExample.Contracts;

public interface IBlobStorageSelector
{
IBlobStorageAction OnBlob(string containerName, string blobName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace FluentInterfaceLibrary.BlobStorageExample.Contracts;

public interface IRead
{
void FromFile(string filePath);
void FromStream(Stream stream);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace FluentInterfaceLibrary.BlobStorageExample.Contracts;

public interface IWrite
{
void ToFolder(string folderPath);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using BuildingBlocks;
using FluentInterfaceLibrary.BlobStorageExample;
using FluentInterfaceLibrary.LinqExample;

namespace FluentInterfaceLibrary;

public class Executor : PatternExecutor
{
public override string Name => "Fluent Interface - Creational Pattern";

public override void Execute()
{
BlobStorageExecutor.Execute();
LinqExecutor.Execute();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\BuildingBlocks.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using BuildingBlocks;

namespace FluentInterfaceLibrary.LinqExample;

public static class LinqExecutor
{
public static void Execute()
{
ConsoleExtension.WriteSeparator("LINQ example");

var englishToSerbianDictionary = new Dictionary<string, string>
{
{"adventure", "avantura"},
{"bird", "ptica"},
{"fish", "riba"},
{"football", "fudbal"},
{"programming", "programiranje"},
};

DisplayDictionary(englishToSerbianDictionary);

Console.WriteLine("\nFinding translations for English words containing the letter 'a', sorted by length and displayed in uppercase...");
FindTranslationsProgressively(englishToSerbianDictionary);
FindTranslationsUsingFluentInterface(englishToSerbianDictionary);
}

private static void DisplayDictionary(Dictionary<string, string> englishToSerbianDictionary)
{
Console.WriteLine("\nContent of the dictionary: ");
foreach (var (englishWord, serbianWord) in englishToSerbianDictionary)
{
Console.WriteLine($"{englishWord} - {serbianWord}");
}
}

private static void FindTranslationsProgressively(Dictionary<string, string> englishToSerbianDictionary)
{
var filtered = englishToSerbianDictionary.Where(t => t.Key.Contains("a"));
var sorted = filtered.OrderBy(t => t.Value.Length);
var finalTranslations = sorted.Select(t => t.Value.ToUpper());

Console.WriteLine("\nProgressive translations: ");
DisplayWords(finalTranslations);
}

private static void FindTranslationsUsingFluentInterface(Dictionary<string, string> englishToSerbianDictionary)
{
// C# uses fluent programming extensively in LINQ to build queries using "standard query operators".
var finalTranslations = englishToSerbianDictionary
.Where(t => t.Key.Contains("a"))
.OrderBy(t => t.Value.Length)
.Select(t => t.Value.ToUpper());

Console.WriteLine("\nFluent interface translations: ");
DisplayWords(finalTranslations);
}

private static void DisplayWords(IEnumerable<string> words)
{
foreach (var word in words)
{
Console.WriteLine(word);
}
}
}
14 changes: 12 additions & 2 deletions src/DesignPatternsLibrary.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30711.63
# Visual Studio Version 17
VisualStudioVersion = 17.2.32616.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DesignPatternsLibrary", "DesignPatternsLibrary\DesignPatternsLibrary.csproj", "{60C3B7E4-E290-47F4-A3C0-4F9505DE926F}"
EndProject
Expand Down Expand Up @@ -141,6 +141,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specification", "Specificat
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProductSpecification", "AdditionalPatterns\Specification\ProductSpecification\ProductSpecification.csproj", "{82AC53EE-D473-4826-9443-133BED0C830C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FluentInterface", "FluentInterface", "{6CBEFB1A-4A1D-4F1E-8878-C6AE19444596}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentInterfaceLibrary", "AdditionalPatterns\FluentInterface\FluentInterfaceLibrary\FluentInterfaceLibrary.csproj", "{97D64ED5-9F0A-437A-A96C-D338D0CAE390}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -283,6 +287,10 @@ Global
{82AC53EE-D473-4826-9443-133BED0C830C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{82AC53EE-D473-4826-9443-133BED0C830C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{82AC53EE-D473-4826-9443-133BED0C830C}.Release|Any CPU.Build.0 = Release|Any CPU
{97D64ED5-9F0A-437A-A96C-D338D0CAE390}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{97D64ED5-9F0A-437A-A96C-D338D0CAE390}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97D64ED5-9F0A-437A-A96C-D338D0CAE390}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97D64ED5-9F0A-437A-A96C-D338D0CAE390}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -351,6 +359,8 @@ Global
{BBDF24BC-7D73-4A8F-B8B0-C649019E1B80} = {C8512694-34BF-4616-A82F-12D73DE8263A}
{F1704AAE-3951-43C1-85EC-16C4C52133F3} = {15B15698-CB27-4F30-884C-25C2C1D50202}
{82AC53EE-D473-4826-9443-133BED0C830C} = {F1704AAE-3951-43C1-85EC-16C4C52133F3}
{6CBEFB1A-4A1D-4F1E-8878-C6AE19444596} = {15B15698-CB27-4F30-884C-25C2C1D50202}
{97D64ED5-9F0A-437A-A96C-D338D0CAE390} = {6CBEFB1A-4A1D-4F1E-8878-C6AE19444596}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F9238CD9-9068-4B63-8D9E-53684BA60A21}
Expand Down
1 change: 1 addition & 0 deletions src/DesignPatternsLibrary/DesignPatternsLibrary.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

<ItemGroup>
<ProjectReference Include="..\AdditionalPatterns\EventAggregator\StoreManagement\StoreManagement.csproj" />
<ProjectReference Include="..\AdditionalPatterns\FluentInterface\FluentInterfaceLibrary\FluentInterfaceLibrary.csproj" />
<ProjectReference Include="..\AdditionalPatterns\Interpreter\InterpreterLibrary\InterpreterLibrary.csproj" />
<ProjectReference Include="..\AdditionalPatterns\LazyLoad\LazyLoadLibrary\LazyLoadLibrary.csproj" />
<ProjectReference Include="..\AdditionalPatterns\NullObject\NullObjectLibrary\NullObjectLibrary.csproj" />
Expand Down
1 change: 1 addition & 0 deletions src/DesignPatternsLibrary/PatternExecutorsRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ private PatternExecutorsRegistry()
new CompositeLibrary.Executor(),
new DecoratorLibrary.Executor(),
new FacadeLibrary.Executor(),
new FluentInterfaceLibrary.Executor(),
new FlyweightLibrary.Executor(),
new ProxyLibrary.Executor(),
new ChainOfResponsibilityLibrary.Executor(),
Expand Down

0 comments on commit 7ae7dce

Please sign in to comment.