├── .gitignore
├── AbstractFactory
├── AbstractFactory.csproj
├── App.config
├── IProduct.cs
├── MyAbstractFactory.cs
├── ProductOne.cs
├── ProductThree.cs
├── ProductTwo.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── README.md
├── C.Sharp.Patterns.sln
├── CompositionOverInheritance
├── App.config
├── Composed
│ ├── EnhancedOrder1Composed.cs
│ └── EnhancedOrder1ComposedAlternate.cs
├── CompositionOverInheritance.csproj
├── IAddress.cs
├── IOrder.cs
├── IOrderEnhanced.cs
├── Inherited
│ ├── EnhancedOrder1Inherited.cs
│ └── Order1.cs
├── OrderProcessor.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── README.md
├── DbVersionMigration
├── App.config
├── DbVersionMigration.csproj
├── IMigration.cs
├── IMigrationManager.cs
├── MigrationManager.cs
├── Migrations
│ ├── MigrationAttribute.cs
│ ├── Version0001.cs
│ ├── Version0002.cs
│ └── Version0003.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── README.md
├── Decorator
├── App.config
├── CachedFoo.cs
├── Decorator.csproj
├── Foo.cs
├── IDoSomething.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── README.md
├── Delegate
├── App.config
├── ClassThatTakesDelegateOne.cs
├── ClassThatTakesDelegateThree.cs
├── ClassThatTakesDelegateTwo.cs
├── Delegate.csproj
├── Model
│ └── Foo.cs
├── NoDelegate.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── README.md
├── DistributedLocking
├── DistributedLocking.csproj
├── Locking
│ ├── DatabaseDistributedLocked.cs
│ ├── IDistributedLock.cs
│ └── InMemoryDistributedLock.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── README.md
├── DynamicDispatch
├── App.config
├── DynamicDispatch.csproj
├── Messaging
│ ├── Dispatching
│ │ ├── IDispatchMessages.cs
│ │ └── MessageDispatcher.cs
│ ├── Handlers
│ │ ├── BarHandler.cs
│ │ ├── FooHandler.cs
│ │ └── IHandleMessages.cs
│ ├── Helpers
│ │ └── MessageHelper.cs
│ └── Messages
│ │ ├── BarMessage.cs
│ │ ├── FooMessage.cs
│ │ └── IMessage.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── README.md
├── Observer
├── App.config
├── BubbleObserver.cs
├── ICanBeObserved.cs
├── Observer.csproj
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── README.md
├── SomethingAmazingEventArgs.cs
├── UnicornDancer.cs
└── UnicornObserver.cs
├── Pipeline
├── App.config
├── IPipeline.cs
├── ImagePipeline.cs
├── PipeModel
│ ├── IModel.cs
│ └── ImageModel.cs
├── Pipeline.csproj
├── Pipes
│ ├── CropAction.cs
│ ├── DesaturateAction.cs
│ ├── IPipe.cs
│ └── RotateAction.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── README.md
├── PoorMansDependencyInjection
├── App.config
├── Bar.cs
├── Bar2.cs
├── BestFoo.cs
├── BetterFoo.cs
├── BetterFoo2.cs
├── Foo.cs
├── IBar.cs
├── IFoo.cs
├── PoorMansDependencyInjection.csproj
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── README.md
├── README.md
├── SimpleFactory
├── App.config
├── Bar.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── README.md
└── SimpleFactory.csproj
├── Singleton
├── App.config
├── NonThreadSafeSingleton.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── README.md
├── Singleton.csproj
└── ThreadSafeSingleton.cs
├── Solid
├── App.config
├── EmailDependency.cs
├── IEmailDependency.cs
├── ISolidEmailer.cs
├── ISolidUserRecordUpdater.cs
├── IUpdateUserRecords.cs
├── NotSolid.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── README.md
├── Solid.csproj
├── SolidEmailer.cs
├── SolidUserRecordUpdater.cs
└── UpdateUserRecords.cs
├── Strategy
├── App.config
├── BadGuy.cs
├── BadGuyFactory.cs
├── Henchman.cs
├── IBadGuy.cs
├── MovementStrategies
│ ├── IMovementStrategy.cs
│ ├── SideToSideStrategy.cs
│ └── UpAndDownStrategy.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── README.md
└── Strategy.csproj
├── Tasks
├── App.config
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── Tasks.csproj
└── Work
│ └── DoSomething.cs
├── Threading
├── App.config
├── IManageThreads.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── README.md
├── ThreadManager.cs
├── Threading.csproj
└── Work
│ ├── DoSomething.cs
│ └── IDoSomething.cs
├── UnitOfWork
├── App.config
├── DAL
│ ├── IUnitOfWork.cs
│ ├── MockUnitOfWork.cs
│ ├── PetaPoco.cs
│ ├── PetaPocoUnitOfWork.cs
│ └── UnitOfWorkFactory.cs
├── Models
│ ├── MyDbEntity.cs
│ └── MyOtherDbEntity.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── README.md
├── Repositories
│ ├── BarRepository.cs
│ └── FooRepository.cs
├── UnitOfWork.csproj
└── packages.config
└── Visitor
├── AnotherBar.cs
├── App.config
├── Bar.cs
├── Component1.cs
├── Component2.cs
├── Component3.cs
├── Foo.cs
├── IFoo.cs
├── ISaySomething.cs
├── IVisitFoo.cs
├── IVisitable.cs
├── Program.cs
├── Properties
└── AssemblyInfo.cs
├── README.md
└── Visitor.csproj
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .AppleDouble
3 | .LSOverride
4 | Icon
5 | ._*
6 | .Spotlight-V100
7 | .Trashes
8 | Thumbs.db
9 | ehthumbs.db
10 | Desktop.ini
11 | $RECYCLE.BIN/
12 |
13 | app/less/*.css
14 | dist/
15 | node_modules/
16 | tmp/
17 | pkg/*.nupkg
18 | pkg/*.zip
19 |
20 | *_versioned*
21 |
22 |
23 | [Dd]ebug/
24 | [Bb]uild/
25 | [Rr]elease/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Pp]ackages/
29 | VersionInfo.cs
30 |
31 | *.aps
32 | *.bak
33 | *.backup
34 | *.build.csdef
35 | *.cache
36 | *.ilk
37 | *.lib
38 | *.log
39 | *.meta
40 | *.ncb
41 | *.obj
42 | *.orig
43 | *.pch
44 | *.pdb
45 | *.pgc
46 | *.pgd
47 | *.rsp
48 | *.sbr
49 | *.scc
50 | *.sdf
51 | *.sln.docstates
52 | *.suo
53 | *.tlb
54 | *.tlh
55 | *.tli
56 | *.tmp
57 | *.user
58 | *.vspscc
59 | *.[Pp]ublish.xml
60 | *_i.c
61 | *_p.c
62 | .builds
63 | .svn
64 | .hg
65 | .hgignore
--------------------------------------------------------------------------------
/AbstractFactory/AbstractFactory.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {0D318994-0B51-4DF6-99AB-2C94FAB46A3B}
8 | Exe
9 | Properties
10 | AbstractFactory
11 | AbstractFactory
12 | v4.5
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
64 |
--------------------------------------------------------------------------------
/AbstractFactory/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/AbstractFactory/IProduct.cs:
--------------------------------------------------------------------------------
1 | namespace AbstractFactory
2 | {
3 | //an interface (or abstract class) is used to allow us to not commit to a particular concrete class until we decided in our factory
4 | public interface IProduct
5 | {
6 | void MyMethod();
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/AbstractFactory/MyAbstractFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | namespace AbstractFactory
4 | {
5 | public class MyAbstractFactory
6 | {
7 | //an abstract factory only needs one method that returns an abstraction
8 | public static IProduct Create()
9 | {
10 | //as long as it returns an 'IProduct', the client application doesn't care which concrete class is returned.
11 | return new ProductTwo();
12 | }
13 |
14 | //however to make it more useful, a creation method could react to a condition
15 | public static IProduct CreateBasedOnConfig()
16 | {
17 | var setting = ConfigurationManager.AppSettings["ProductType"];
18 |
19 | switch(setting)
20 | {
21 | case "1":
22 | return new ProductOne();
23 |
24 | case "2":
25 | return new ProductTwo();
26 |
27 | case "3":
28 | return new ProductThree();
29 |
30 | default:
31 | return new ProductTwo();
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/AbstractFactory/ProductOne.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AbstractFactory
4 | {
5 | //a generic model that implements IProduct
6 | public class ProductOne : IProduct
7 | {
8 | public void MyMethod()
9 | {
10 | Console.WriteLine("Invoking method from product 1!");
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/AbstractFactory/ProductThree.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AbstractFactory
4 | {
5 | //a generic model that implements IProduct
6 | public class ProductThree : IProduct
7 | {
8 | public void MyMethod()
9 | {
10 | Console.WriteLine("Invoking method from product 3!");
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/AbstractFactory/ProductTwo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AbstractFactory
4 | {
5 | //a generic model that implements IProduct
6 | public class ProductTwo : IProduct
7 | {
8 | public void MyMethod()
9 | {
10 | Console.WriteLine("Invoking method from product 2!");
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/AbstractFactory/Program.cs:
--------------------------------------------------------------------------------
1 | namespace AbstractFactory
2 | {
3 | class Program
4 | {
5 | static void Main(string[] args)
6 | {
7 | //if a model is public, we could just 'new' a product
8 | ProductOne myNewProduct = new ProductOne();
9 | myNewProduct.MyMethod();
10 |
11 | //but if we need to use a new product model (ProductTwo), we'll have to change all instances where 'ProductOne' is used.
12 | //This may not be a problem if you control all of the code, but what if your code is used by a third-party?
13 | //If that happens, you'd have to ask them to replace all references to 'ProductOne' and substitute 'ProductTwo' in their application and then recompile.
14 | //This is not ideal, we'd really like our client to not have to do anything with their code.
15 |
16 | //If you use an interface and a factory, your client wouldn't have to change any of their code because the client delegates the creation of
17 | //the product to the factory. Therefore as long as the factory returns an 'IProduct', the fact that it's returning 'ProductTwo' or 'ProductThree' matters not to
18 | //the client application.
19 |
20 | //this returns ProductTwo but could just as well return ProductThree and no changes would be needed to the code below.
21 | IProduct myFactoryProduct = MyAbstractFactory.Create();
22 | myFactoryProduct.MyMethod();
23 |
24 | //we can make this even sexier if we want some sort of condition to dictate what the factory should return
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/AbstractFactory/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ConsoleApplication1")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("University of Notre Dame")]
12 | [assembly: AssemblyProduct("ConsoleApplication1")]
13 | [assembly: AssemblyCopyright("Copyright © University of Notre Dame 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("2d4f04cd-22ae-458e-bf0d-5d95427eabb9")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/AbstractFactory/README.md:
--------------------------------------------------------------------------------
1 | # Abstract Factory
2 |
3 | ## Problem it solves
4 | Super similar to the [Simple Factory](../SimpleFactory/README.md) except the `.Create()` method returns an abstraction.
5 |
6 | ## Common Uses
7 | The advantage over the Simple Factory is that this only depends on an abstraction whereas the Simple Factory depends on a concrete class.
8 |
9 | Check out a video I made on factories: https://www.youtube.com/watch?v=qzxp9p7UP_Y.
--------------------------------------------------------------------------------
/CompositionOverInheritance/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CompositionOverInheritance/Composed/EnhancedOrder1Composed.cs:
--------------------------------------------------------------------------------
1 | namespace CompositionOverInheritance.Composed
2 | {
3 | public class EnhancedOrder1Composed : IOrder, IOrderEnhanced
4 | {
5 | //this class uses composotion and essentially does repeat what Order1 is doing but is now free to evolve independently
6 | //this could be good or bad but this is the design decision you make by doing compostion vs inheritance
7 | public int OrderNumber { get; }
8 | public IAddress ShippingAddress { get; }
9 | public IAddress BillingAddress { get; }
10 | public string EnhancedField { get; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/CompositionOverInheritance/Composed/EnhancedOrder1ComposedAlternate.cs:
--------------------------------------------------------------------------------
1 | namespace CompositionOverInheritance.Composed
2 | {
3 | public class EnhancedOrder1ComposedAlternate : IOrderEnhanced
4 | {
5 | public IOrder Order;
6 | public string EnhancedField { get; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/CompositionOverInheritance/CompositionOverInheritance.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {9381B46C-2EF6-434C-B1E9-A3853DA3190D}
8 | Exe
9 | Properties
10 | CompositionOverInheritance
11 | CompositionOverInheritance
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
69 |
--------------------------------------------------------------------------------
/CompositionOverInheritance/IAddress.cs:
--------------------------------------------------------------------------------
1 | namespace CompositionOverInheritance
2 | {
3 | public interface IAddress
4 | {
5 | string Address1 { get; }
6 | string Address2 { get; }
7 | string City { get; }
8 | string State { get; }
9 | string Zip { get; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/CompositionOverInheritance/IOrder.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 |
3 | namespace CompositionOverInheritance
4 | {
5 | public interface IOrder
6 | {
7 | int OrderNumber { get; }
8 | IAddress ShippingAddress { get; }
9 | IAddress BillingAddress { get; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/CompositionOverInheritance/IOrderEnhanced.cs:
--------------------------------------------------------------------------------
1 | namespace CompositionOverInheritance
2 | {
3 | public interface IOrderEnhanced
4 | {
5 | string EnhancedField { get; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/CompositionOverInheritance/Inherited/EnhancedOrder1Inherited.cs:
--------------------------------------------------------------------------------
1 | namespace CompositionOverInheritance.Inherited
2 | {
3 | public class EnhancedOrder1Inherited : Order1, IOrderEnhanced
4 | {
5 | //this class will be tied to any changes that happens in Order1 which could be good or bad
6 | //but this is the design\decision pattern you make when you use inheritance
7 | public string EnhancedField { get; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/CompositionOverInheritance/Inherited/Order1.cs:
--------------------------------------------------------------------------------
1 | namespace CompositionOverInheritance.Inherited
2 | {
3 | public class Order1 : IOrder
4 | {
5 | public int OrderNumber { get; }
6 | public IAddress ShippingAddress { get; }
7 | public IAddress BillingAddress { get; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/CompositionOverInheritance/OrderProcessor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CompositionOverInheritance
4 | {
5 | public class OrderProcessor
6 | {
7 | public void Process(IOrder order)
8 | {
9 | Console.WriteLine("Processing an IOrder...");
10 | }
11 |
12 | public void Process(IOrderEnhanced order)
13 | {
14 | Console.WriteLine("Processing an IEnhancedOrder...");
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/CompositionOverInheritance/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CompositionOverInheritance.Composed;
3 | using CompositionOverInheritance.Inherited;
4 |
5 | namespace CompositionOverInheritance
6 | {
7 | class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 | /*
12 | * You've heard it before 'favor composition over inheritance.
13 | * I take that to mean tha when you inherit, you are making a lot of assumptions about the future. When the 'base' class evolves, so will
14 | * everything 'downstream' on the inheritance chain. This could be desirable or it could create a bunch of un-used properties when dealing
15 | * with edge cases.
16 | *
17 | * I tend to prefer composition. Meaning that rather than inherit, I just implement an inteface multiple times to better tailor the modeling.
18 | * In this way you can evolve each piece separately and not have to deal with the baggage of inheritance.
19 | */
20 |
21 | //the base class
22 | var order1 = new Order1();
23 |
24 | //inheritance
25 | var order1EnhancedInherited = new EnhancedOrder1Inherited();
26 |
27 | //composition
28 | var order1EnhancedComposed = new EnhancedOrder1Composed();
29 |
30 | //another way to compose is to wrap the original that I tend to prefer
31 | var order1EnhancedComposedAlternate = new EnhancedOrder1ComposedAlternate
32 | {
33 | Order = new Order1()
34 | };
35 |
36 |
37 | //only to show each one being used
38 | var processor = new OrderProcessor();
39 |
40 | //base
41 | processor.Process(order1);
42 |
43 | //inherited
44 | processor.Process((IOrder)order1EnhancedInherited);
45 | processor.Process((IOrderEnhanced)order1EnhancedInherited);
46 |
47 | //composed
48 | processor.Process((IOrder)order1EnhancedComposed);
49 | processor.Process((IOrderEnhanced)order1EnhancedComposed);
50 |
51 | //composed alternate
52 | processor.Process(order1EnhancedComposedAlternate.Order);
53 | processor.Process(order1EnhancedComposedAlternate);
54 |
55 | Console.ReadKey();
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/CompositionOverInheritance/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("CompositionOverInheritance")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("CompositionOverInheritance")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("9381b46c-2ef6-434c-b1e9-a3853da3190d")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/CompositionOverInheritance/README.md:
--------------------------------------------------------------------------------
1 | # Composition over Inheritance
2 |
3 | ## Problem it solves
4 | Inheritance isn't evil, but it does lock a class to another classes' destiny. Over-use of inheritance can lead to classes with default values\null as the 'norm' rather than having a class that models exactly what is needed.
5 |
6 | Further when such classes become bloated, serialization and persistence do more work than they need to.
7 |
8 | I prefer to compose a class by assembling it's parts and implement a common interface. While it may be more work upfront to model, it'll keep things nice and tidy.
9 |
10 | ## Common Uses
11 | Often time as a business evolves, there is a natural tendency to simply 'just add a new field' to a class to cover the new requirement or create a derived class and add properties there.
12 |
13 | If an ordering scheme requires new functionality, you should consider composing a new object rather than inherit and add properties. Inheritance should be used in moderation.
14 |
--------------------------------------------------------------------------------
/DbVersionMigration/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/DbVersionMigration/DbVersionMigration.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {52D003BC-DA68-4BF6-A46F-07923FF2885E}
8 | Exe
9 | Properties
10 | DbVersionMigration
11 | DbVersionMigration
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
67 |
--------------------------------------------------------------------------------
/DbVersionMigration/IMigration.cs:
--------------------------------------------------------------------------------
1 | namespace DbVersionMigration
2 | {
3 | //define what each migration should do
4 | //you may want to do upgrades only
5 | public interface IMigration
6 | {
7 | void Up();
8 | void Down();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/DbVersionMigration/IMigrationManager.cs:
--------------------------------------------------------------------------------
1 | namespace DbVersionMigration
2 | {
3 | public interface IMigrationManager
4 | {
5 | //just a single method
6 | void HandleMigrations(int currentDbVersion, int targetVersion);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/DbVersionMigration/MigrationManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using DbVersionMigration.Migrations;
5 |
6 | namespace DbVersionMigration
7 | {
8 | public class MigrationManager : IMigrationManager
9 | {
10 | private readonly Dictionary _migrationLookup = new Dictionary();
11 |
12 | public void HandleMigrations(int currentDbVersion, int targetVersion)
13 | {
14 | //if we don't need to upgrade, let's get out of here
15 | if (currentDbVersion == targetVersion)
16 | {
17 | return;
18 | }
19 |
20 | //grab all of the migrations via reflection
21 | var migrationType = typeof(IMigration);
22 | var migrations = AppDomain.CurrentDomain.GetAssemblies()
23 | .SelectMany(s => s.GetTypes())
24 | .Where(p => migrationType.IsAssignableFrom(p) && !p.IsInterface)
25 | .ToList();
26 | //.Select(t => (IMigration)Activator.CreateInstance(t));
27 |
28 | _migrationLookup.Clear();
29 | _initLookup(migrations);
30 |
31 | try
32 | {
33 | //decide if we're upgrading or downgrading
34 | if (currentDbVersion > targetVersion)
35 | {
36 | _downgrade(migrations, currentDbVersion, targetVersion);
37 | }
38 | else
39 | {
40 | _upgrade(migrations, currentDbVersion, targetVersion);
41 | }
42 | }
43 | catch (Exception ex)
44 | {
45 | //log it
46 | }
47 | }
48 |
49 | private void _upgrade(IEnumerable migrations, int currentDbVersion, int targetVersion)
50 | {
51 | //only run the migrations from a version forward
52 | var migrationsToExecute = migrations
53 | .Where(x => _migrationLookup[x.Name] > currentDbVersion && _migrationLookup[x.Name] <= targetVersion)
54 | .OrderBy(x => _migrationLookup[x.Name]);
55 |
56 | foreach (var migration in migrationsToExecute)
57 | {
58 | var migrationInstance = (IMigration)Activator.CreateInstance(migration);
59 |
60 | Console.WriteLine($"Upgrading to v{_migrationLookup[migration.Name]}...");
61 |
62 | migrationInstance.Up();
63 | }
64 | }
65 |
66 | private void _downgrade(IEnumerable migrations, int currentDbVersion, int targetVersion)
67 | {
68 | //only run the migrations for a version backwards
69 | var migrationsToExecute = migrations
70 | .Where(x => _migrationLookup[x.Name] < currentDbVersion && _migrationLookup[x.Name] >= targetVersion)
71 | .OrderByDescending(x => _migrationLookup[x.Name]);
72 |
73 | foreach (var migration in migrationsToExecute)
74 | {
75 | var migrationInstance = (IMigration)Activator.CreateInstance(migration);
76 |
77 | Console.WriteLine($"Downgrading to v{_migrationLookup[migration.Name]}...");
78 |
79 | migrationInstance.Down();
80 | }
81 | }
82 |
83 | private void _initLookup(IEnumerable types)
84 | {
85 | foreach (var type in types)
86 | {
87 | if (_migrationLookup.ContainsKey(type.Name))
88 | {
89 | throw new Exception($"Looks like you have multiple types with the same name {type.Name}!");
90 | }
91 |
92 | var version = _getMigrationVersion(type);
93 |
94 | if (version == null)
95 | {
96 | throw new Exception($"Looks like you have multiple types with the same name {type.FullName}!");
97 | }
98 |
99 | if (_migrationLookup.ContainsValue(version.Value))
100 | {
101 | throw new Exception($"Looks like you have multiple types with the same version: {version}!");
102 | }
103 |
104 | _migrationLookup.Add(type.Name, version.Value);
105 | }
106 | }
107 |
108 | private int? _getMigrationVersion(Type type)
109 | {
110 | int? version = null;
111 |
112 | var attributes = type.GetCustomAttributes(false);
113 |
114 | foreach (var attribute in attributes)
115 | {
116 | var migrationAttribute = attribute as MigrationAttribute;
117 |
118 | if (migrationAttribute != null)
119 | {
120 | version = migrationAttribute.Version;
121 | }
122 | }
123 |
124 | return version;
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/DbVersionMigration/Migrations/MigrationAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace DbVersionMigration.Migrations
4 | {
5 | [AttributeUsage(AttributeTargets.Class)]
6 | public class MigrationAttribute : Attribute
7 | {
8 | public int Version { get; set; }
9 |
10 | public MigrationAttribute(int version)
11 | {
12 | Version = version;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/DbVersionMigration/Migrations/Version0001.cs:
--------------------------------------------------------------------------------
1 | namespace DbVersionMigration.Migrations
2 | {
3 | //a simple migration
4 | [Migration(1)]
5 | public class Version0001 : IMigration
6 | {
7 | //run you custom sql here in a transaction
8 | public void Up()
9 | {
10 | //put your sql stuff here in a transaction
11 | }
12 |
13 | public void Down()
14 | {
15 | //put your sql stuff here in a transaction
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/DbVersionMigration/Migrations/Version0002.cs:
--------------------------------------------------------------------------------
1 | namespace DbVersionMigration.Migrations
2 | {
3 | //another simple migration
4 | [Migration(2)]
5 | public class Version0002 : IMigration
6 | {
7 | public void Up()
8 | {
9 | //put your sql stuff here in a transaction
10 | }
11 |
12 | public void Down()
13 | {
14 | //put your sql stuff here in a transaction
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/DbVersionMigration/Migrations/Version0003.cs:
--------------------------------------------------------------------------------
1 | namespace DbVersionMigration.Migrations
2 | {
3 | //another simple migration
4 | [Migration(3)]
5 | public class Version0003 : IMigration
6 | {
7 | public void Up()
8 | {
9 | //put your sql stuff here in a transaction
10 | }
11 |
12 | public void Down()
13 | {
14 | //put your sql stuff here in a transaction
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/DbVersionMigration/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace DbVersionMigration
4 | {
5 | class Program
6 | {
7 | static void Main(string[] args)
8 | {
9 | /*
10 | * The idea here is to only modify the DB via a migration.
11 | *
12 | * In this way you can easily keep track of what state a DB schema should be in.
13 | *
14 | * You can also run this against the dev, prod, staging DB's to ensure they are all a particular version.
15 | */
16 |
17 | //you'll want to store the 'current DB version' somewhere
18 | //this could be the web config, or a DB table
19 | var currentDbVersion = 0;
20 |
21 | //we'll use our manager to do the dirty work
22 | //you can run this on startup or you can run this explicitly
23 | var migrationManager = new MigrationManager();
24 |
25 | //simply use integers for versioning
26 | //here we're going from v0 to v1
27 | migrationManager.HandleMigrations(currentDbVersion, 1);
28 | currentDbVersion = 1;
29 |
30 | //here we go from v1 to v2
31 | migrationManager.HandleMigrations(currentDbVersion, 2);
32 | currentDbVersion = 2;
33 |
34 | //let's go back to v1
35 | //be careful if you choose to allow for downgrades as you could drop a table
36 | //you might just want to 'go forward' with upgrades only
37 | //maybe leave table drops as a manual process
38 | migrationManager.HandleMigrations(currentDbVersion, 1);
39 | currentDbVersion = 1;
40 |
41 | //let's go all the way up
42 | migrationManager.HandleMigrations(currentDbVersion, 3);
43 | currentDbVersion = 3;
44 |
45 | //all the way down
46 | migrationManager.HandleMigrations(currentDbVersion, 0);
47 | currentDbVersion = 0;
48 |
49 |
50 | Console.ReadKey();
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/DbVersionMigration/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("DbVersionMigration")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("DbVersionMigration")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("52d003bc-da68-4bf6-a46f-07923ff2885e")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/DbVersionMigration/README.md:
--------------------------------------------------------------------------------
1 | # DB Version Migration
2 |
3 | ## Problem it solves
4 | When sync'ing DB schemas against code or across environments, it's good to have a mechanism to version and upgrade (or even downgrade) a schema via a script.
5 |
6 | ## Common Uses
7 | If you alter the Dev DB but now need to deploy your schema, this is very useful if using a micro-ORM and now need to deploy the schema changes to one or more DB servers without manually doing so.
8 |
--------------------------------------------------------------------------------
/Decorator/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Decorator/CachedFoo.cs:
--------------------------------------------------------------------------------
1 | namespace Decorator
2 | {
3 | class CachedFoo : IDoSomething
4 | {
5 | private readonly IDoSomething _nonCachedFoo;
6 | private string _whatIdid;
7 |
8 | public CachedFoo(IDoSomething nonCachedFoo)
9 | {
10 | _nonCachedFoo = nonCachedFoo;
11 | }
12 |
13 | public string DoSomething()
14 | {
15 | //if i don't have the stored item, go get it from the class I don't have the source
16 | if (string.IsNullOrEmpty(_whatIdid))
17 | {
18 | _whatIdid = _nonCachedFoo.DoSomething();
19 | }
20 |
21 | return _whatIdid;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Decorator/Decorator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {15C1C244-A7BE-4AD1-B9AC-C814ED39E025}
8 | Exe
9 | Properties
10 | Decorator
11 | Decorator
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
63 |
--------------------------------------------------------------------------------
/Decorator/Foo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Decorator
4 | {
5 | //imagine this class contains highly secretive source code and is closed from prying eyes!
6 | class Foo : IDoSomething
7 | {
8 | public string DoSomething()
9 | {
10 | return "I just did something!";
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Decorator/IDoSomething.cs:
--------------------------------------------------------------------------------
1 | namespace Decorator
2 | {
3 | interface IDoSomething
4 | {
5 | string DoSomething();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Decorator/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Decorator
4 | {
5 | class Program
6 | {
7 | static void Main(string[] args)
8 | {
9 | //the decorator pattern is used to augment an existing implementation without creating overriding behavior in the traditional sense
10 |
11 | //if we have class foo we might use it simply like so:
12 | var foo = new Foo();
13 |
14 | foo.DoSomething();
15 |
16 | //however eventually we may want to add caching to this class method
17 | //we could update the class to support it directly, but what happens if we DON'T have the code access?
18 | //we could use the decorator pattern to simply augment it
19 | //for this part, assume that class Foo is in another assembly and locked away as closed source
20 |
21 | //this is most elegant when the class you want to augment implements an interface. In our case Foo impl IDoSomething.
22 | //using this pattern with an abstraction allows your new class to work like the old class with augmentation
23 |
24 | var cachedFoo = new CachedFoo(foo);
25 | Console.WriteLine(cachedFoo.DoSomething()); //will invoke the inner IDoSomething (Foo class)
26 | Console.WriteLine(cachedFoo.DoSomething()); //this will output the cached value the second time
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Decorator/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Decorator")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Decorator")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("15c1c244-a7be-4ad1-b9ac-c814ed39e025")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Decorator/README.md:
--------------------------------------------------------------------------------
1 | # Decorator
2 |
3 | ## Problem it solves
4 | If you have a class that you cannot (no access to source) or do not want to alter (pick a reason), you can still augment a method even if it is not marked `virtual` provided the class to be augmented uses an abstraction.
5 |
6 | ## Common Uses
7 | Let's say you have a third party class that is closed source. You want one of the methods to be able to return cached responses. The method in question is not marked virtual but the class itself uses a public interface. The example contained here shows how to do such a task.
8 |
9 | This pattern is also known as the wrapper pattern.
10 |
11 | Here's a video I've put together for you to help out: https://www.youtube.com/watch?v=brsmxMkTM8I
12 |
--------------------------------------------------------------------------------
/Delegate/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Delegate/ClassThatTakesDelegateOne.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Delegate
4 | {
5 | //this class doesn't know anything about the Foo class
6 | //it won't care if Foo goes away, never existed at all or what planet it was born on
7 | class ClassThatTakesDelegateOne
8 | {
9 | //we can pass in a function to be run, this uses 'Action' which has no return type and no args
10 | public void DoMagicalThings(Action aFunctionWeCanRun)
11 | {
12 | aFunctionWeCanRun();
13 | }
14 |
15 | //we can pass in a function to be run, this uses 'Action' which has no return type but takes an arg (bool in this case)
16 | //you can have more than one passed in arg
17 | public void DoMagicalThings(Action aFunctionWeCanRun)
18 | {
19 | aFunctionWeCanRun(true);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Delegate/ClassThatTakesDelegateThree.cs:
--------------------------------------------------------------------------------
1 | namespace Delegate
2 | {
3 | //you can even name a delegate (old school way :) )
4 | delegate int OldSchoolNamedDelegate(bool someBool);
5 |
6 | class ClassThatTakesDelegateThree
7 | {
8 | //some like it named as such
9 | public void DoMagicalThings(OldSchoolNamedDelegate aFunctionWeCanRun)
10 | {
11 | //based on the signature of the Func, we are passing a bool to the function and expecting an int to be returned
12 | int result = aFunctionWeCanRun(true);
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Delegate/ClassThatTakesDelegateTwo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Delegate
4 | {
5 | //this class doesn't know anything about the Foo class
6 | //it won't care if Foo goes away, never existed at all or what planet it was born on
7 | class ClassThatTakesDelegateTwo
8 | {
9 | //we can pass in a function to be run, this uses 'Func' which has both a return type and an arg list
10 | public void DoMagicalThings(Func aFunctionWeCanRun)
11 | {
12 | //based on the signature of the Func, we are passing a bool to the function and expecting an int to be returned
13 | int result = aFunctionWeCanRun(true);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Delegate/Delegate.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {CB4DE76E-CC5B-4911-9848-8D5ED09E9B57}
8 | Exe
9 | Properties
10 | Delegate
11 | Delegate
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
65 |
--------------------------------------------------------------------------------
/Delegate/Model/Foo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Delegate.Model
4 | {
5 | class Foo
6 | {
7 | public void DoSomething()
8 | {
9 | Console.WriteLine("I'm doing something!");
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Delegate/NoDelegate.cs:
--------------------------------------------------------------------------------
1 | using Delegate.Model;
2 |
3 | namespace Delegate
4 | {
5 | //this is a normal class that uses Foo to do something
6 | //there is nothing wrong with this code but you should know that this creates a tightly coupled dependency on Foo
7 | //this class knows about Foo. If Foo goes away, this class needs updated
8 | class NoDelegate
9 | {
10 | public void DoMagicalThings()
11 | {
12 | new Foo().DoSomething();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Delegate/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Delegate.Model;
3 |
4 | namespace Delegate
5 | {
6 | class Program
7 | {
8 | static void Main(string[] args)
9 | {
10 | //nothing special here
11 | var noDelegate = new NoDelegate();
12 | noDelegate.DoMagicalThings();
13 |
14 | //pass in a function that has one arg and no return type (void)
15 | var delegateOne = new ClassThatTakesDelegateOne();
16 | delegateOne.DoMagicalThings(aFunctionWeCanRunFromDelegateOne);
17 |
18 | //you can also tighten it up with a lambda to do it inline
19 | delegateOne.DoMagicalThings(someBool =>
20 | {
21 | //this is a delegate function inline
22 | if (someBool)
23 | {
24 | //only the Program class has to know about Foo
25 | new Foo().DoSomething();
26 | }
27 | });
28 |
29 | //what if you want to pass an arg and get a return type?
30 | var delegateTwo = new ClassThatTakesDelegateTwo();
31 | delegateTwo.DoMagicalThings(aFunctionWeCanRunFromDelegateTwo);
32 |
33 | //just like Action, we can use a lambda as well
34 | delegateTwo.DoMagicalThings(someBool =>
35 | {
36 | //this is a delegate function inline
37 | if (someBool)
38 | {
39 | //only the Program class has to know about Foo
40 | new Foo().DoSomething();
41 |
42 | return 1;
43 | }
44 |
45 | return 0;
46 | });
47 |
48 | //finally we have the original delegate from early C# that works just like delegate one or two based on the delegate at the top
49 | //of the ClassThatTakesDelegateThree
50 | var delegateThree = new ClassThatTakesDelegateThree();
51 | delegateThree.DoMagicalThings(aFunctionWeCanRunFromDelegateTwo);
52 | }
53 |
54 | //this is a delegate function
55 | private static int aFunctionWeCanRunFromDelegateTwo(bool someBool)
56 | {
57 | if (someBool)
58 | {
59 | //only the Program class has to know about Foo
60 | new Foo().DoSomething();
61 |
62 | return 1;
63 | }
64 |
65 | return 0;
66 | }
67 |
68 | //this is a delegate function
69 | private static void aFunctionWeCanRunFromDelegateOne(bool someBool)
70 | {
71 | if (someBool)
72 | {
73 | //only the Program class has to know about Foo
74 | new Foo().DoSomething();
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Delegate/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Delegate")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Delegate")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("cb4de76e-cc5b-4911-9848-8d5ed09e9b57")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Delegate/README.md:
--------------------------------------------------------------------------------
1 | # Delegate
2 |
3 | ## Problem it solves
4 | Some times a class\method can ALMOST be purely free of specific logic but needs a few lines of code to be removed to achieve this. The delegate pattern allows the executing class to execute a passed in method thus relieving a class of having to know specific details of an implementation.
5 |
6 | ## Common Uses
7 | Often times code needs a value and it likely doesn't care how the value is obtained. The delegate pattern can be used to get a value from the UI and not be coupled to any specific UI implementation. This pattern is highly useful to decouple logic from another class.
8 |
--------------------------------------------------------------------------------
/DistributedLocking/DistributedLocking.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {BEFFC0AF-6683-4E0A-8A32-5E99F11C38C4}
8 | Exe
9 | Properties
10 | DistributedLocking
11 | DistributedLocking
12 | v4.5.2
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/DistributedLocking/Locking/DatabaseDistributedLocked.cs:
--------------------------------------------------------------------------------
1 | namespace DistributedLocking.Locking
2 | {
3 | public class DatabaseDistributedLock : IDistributedLock
4 | {
5 | /*
6 | * This assumes you have a column to hold the semaphore and it is unique
7 | *
8 | * Any attempt to insert a semaphore already in the db will fail
9 | *
10 | * Actual db imple left out as it's trivial and based on whatever you want it to be
11 | *
12 | */
13 |
14 | public bool AcquireLock(string semaphore)
15 | {
16 | var alreadyInDb = false; //get from DB
17 |
18 | if (alreadyInDb)
19 | {
20 | return false;
21 | }
22 |
23 | try
24 | {
25 | //insert into db
26 | var wasInserted = true;
27 |
28 | if (wasInserted)
29 | {
30 | //success
31 | return true;
32 | }
33 |
34 | return false;
35 | }
36 | catch
37 | {
38 | return false;
39 | }
40 | }
41 |
42 | public void ReleaseLock(string semaphore)
43 | {
44 | //delete from db
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/DistributedLocking/Locking/IDistributedLock.cs:
--------------------------------------------------------------------------------
1 | namespace DistributedLocking.Locking
2 | {
3 | public interface IDistributedLock
4 | {
5 | bool AcquireLock(string semaphore);
6 | void ReleaseLock(string semaphore);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/DistributedLocking/Locking/InMemoryDistributedLock.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace DistributedLocking.Locking
4 | {
5 | public class InMemoryDistributedLock : IDistributedLock
6 | {
7 | private readonly HashSet _locks = new HashSet();
8 | private readonly object _padLock = new object();
9 |
10 | public bool AcquireLock(string semaphore)
11 | {
12 | lock (_padLock)
13 | {
14 | if (_locks.Contains(semaphore))
15 | {
16 | return false;
17 | }
18 |
19 | try
20 | {
21 | _locks.Add(semaphore);
22 | }
23 | catch
24 | {
25 | return false;
26 | }
27 |
28 | return true;
29 | }
30 | }
31 |
32 | public void ReleaseLock(string semaphore)
33 | {
34 | lock (_padLock)
35 | {
36 | if (_locks.Contains(semaphore))
37 | {
38 | _locks.Remove(semaphore);
39 | }
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/DistributedLocking/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using DistributedLocking.Locking;
5 |
6 | namespace DistributedLocking
7 | {
8 | class Program
9 | {
10 | private static readonly IDistributedLock _locker = new InMemoryDistributedLock();
11 | private static string mySemaphore = "1234customerPayment";
12 | private static Random _random = new Random();
13 |
14 | static void Main(string[] args)
15 | {
16 | while (true)
17 | {
18 | Task.Run(() => _doWork(mySemaphore));
19 |
20 | Thread.Sleep(_random.Next(100, 500));
21 | }
22 | }
23 |
24 | private static void _doWork(string semaphore)
25 | {
26 | var isThreadLocked = _locker.AcquireLock(semaphore);
27 |
28 | Console.WriteLine($"Did we get a lock => {isThreadLocked}");
29 |
30 | if (isThreadLocked)
31 | {
32 | Console.WriteLine("Working...");
33 |
34 | Thread.Sleep(_random.Next(1000, 3000));
35 |
36 | _locker.ReleaseLock(semaphore);
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/DistributedLocking/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("DistributedLocking")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("DistributedLocking")]
13 | [assembly: AssemblyCopyright("Copyright © 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("beffc0af-6683-4e0a-8a32-5e99f11c38c4")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/DistributedLocking/README.md:
--------------------------------------------------------------------------------
1 | # Distributed Locking
2 |
3 | ## Problem it solves
4 | In a single host environment it is a trivial task to ensure that certain bits of code execute one thread at a time using locks. In a horizontally scaled environment, traditional locking will not work as the workers are spread across several nodes and/or processes on the the same node. In order to ensure that only one node may perform a particular task at a given time in a distributed environment, distributed locking can be used.
5 |
6 | Scenario: If a payment collection system is distributed, how can we ensure that we don't accidentally capture a payment twice (e.g. more than one worker is collecting payment for the same customer at the same time)?
7 |
8 | Naive solution: Use a DB to keep track of the current 'status' of a customer. For example we set a status field that says "in progress". Any other processes/nodes check this field before proceeding.
9 |
10 | The problem with the naive solution is that a race condition still exists. Two queue messages could be picked up at the same time and two workers attempt to read the status field and get the OK to proceed b/c the each process have yet to set the status to 'in progress'.
11 |
12 | Distributed locking requires that a semaphore be used to ensure that one and only one process can work a particular message at a time. This can be done with two simple methods:
13 |
14 | ```
15 | bool AcquireLock(string semaphore)
16 | void ReleaseLock(string semaphore)
17 | ```
18 |
19 | The semaphore is a deterministic value based on whatever you want so long as it is unique to the task at hand. Some examples of a good semaphore might be:
20 |
21 | ```
22 | customerId + "collectPayment"
23 | customerId + "refundPayment"
24 | ```
25 |
26 | The actual usage of our distributed lock would be the following pseduocode:
27 |
28 | ```
29 | if(AcquireLock("1234collectPayment")
30 | {
31 | //collectPayment
32 | }
33 | else
34 | {
35 | //could not get a lock, ignore this message as someone else is working on this already
36 | }
37 | ```
38 |
39 | ## Common Uses
40 | The exact implementation of you distributed lock can vary easily. You could choose to:
41 | - Store the key in a SQL DB and require that the key be unique. During lock acquisition, if it fails to insert, then you can fail to get a lock. If you do get a lock, be sure to call `ReleaseLock` in a `finally` statement when finished.
42 | - You could dedicate a single process and store the locks into memory. You'd want to make sure you use traditional locking to force threads to get into a single line.
43 | - You could use a distributed cache like Redis to simply store the lock keys. Existence of a key would equate to a lock already being issued. Simply remove the key from Redis to release the lock. Be sure to account for using traditional locking when setting the locks.
44 |
--------------------------------------------------------------------------------
/DynamicDispatch/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/DynamicDispatch/DynamicDispatch.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {44DD3435-713A-43C5-A35E-E5C56373AB75}
8 | Exe
9 | Properties
10 | DynamicDispatch
11 | DynamicDispatch
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
70 |
--------------------------------------------------------------------------------
/DynamicDispatch/Messaging/Dispatching/IDispatchMessages.cs:
--------------------------------------------------------------------------------
1 | using DynamicDispatch.Messaging.Messages;
2 |
3 | namespace DynamicDispatch.Messaging.Dispatching
4 | {
5 | public interface IDispatchMessages
6 | {
7 | object Dispatch(IMessage message);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/DynamicDispatch/Messaging/Dispatching/MessageDispatcher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DynamicDispatch.Messaging.Helpers;
3 | using DynamicDispatch.Messaging.Messages;
4 |
5 | namespace DynamicDispatch.Messaging.Dispatching
6 | {
7 | public class MessageDispatcher : IDispatchMessages
8 | {
9 | //this class uses a helper for conciseness, but based on the IMessage class name, we can instantiate a handler
10 | //and send the message to it
11 | public object Dispatch(IMessage message)
12 | {
13 | //in the case of 'FooMessage' this will return 'Foo'
14 | var nameOfMessageClass = message.GetType().Name.ToMessageName();
15 |
16 | //this goes and gets the handler type based on the name of the message class
17 | var handlerType = MessageHelper.GetMessageHandlerByMessageTypeName(nameOfMessageClass);
18 |
19 | //this creates an instance at runtime
20 | var handlerInstance = Activator.CreateInstance(handlerType);
21 |
22 | //finally we execute the new handler instance
23 | //i'm not a fan of the 'dynamic' keyword but it works well here
24 | return ((dynamic) handlerInstance).Handle((dynamic)message);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/DynamicDispatch/Messaging/Handlers/BarHandler.cs:
--------------------------------------------------------------------------------
1 | using DynamicDispatch.Messaging.Messages;
2 |
3 | namespace DynamicDispatch.Messaging.Handlers
4 | {
5 | public class BarHandler : IHandleMessages
6 | {
7 | //fully typed for the message type
8 | public object Handle(BarMessage message)
9 | {
10 | return $"Hello from the Bar handler: {message.Age}";
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/DynamicDispatch/Messaging/Handlers/FooHandler.cs:
--------------------------------------------------------------------------------
1 | using DynamicDispatch.Messaging.Messages;
2 |
3 | namespace DynamicDispatch.Messaging.Handlers
4 | {
5 | public class FooHandler : IHandleMessages
6 | {
7 | //fully typed for the message type
8 | public object Handle(FooMessage message)
9 | {
10 | return $"Hello from the Foo Handler! Foo Id: {message.FooId} {message.Name}";
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/DynamicDispatch/Messaging/Handlers/IHandleMessages.cs:
--------------------------------------------------------------------------------
1 | using DynamicDispatch.Messaging.Messages;
2 |
3 | namespace DynamicDispatch.Messaging.Handlers
4 | {
5 | public interface IHandleMessages where TMessage : IMessage
6 | {
7 | object Handle(TMessage message);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/DynamicDispatch/Messaging/Helpers/MessageHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using DynamicDispatch.Messaging.Handlers;
5 |
6 | namespace DynamicDispatch.Messaging.Helpers
7 | {
8 | public static class MessageHelper
9 | {
10 | private static IEnumerable _allHandlerTypes;
11 | private static string _handlerSuffix = "Handler";
12 | private static string _messageSuffix = "Message";
13 |
14 | //based on message name, fine the corresponding handler type
15 | public static Type GetMessageHandlerByMessageTypeName(string messageType)
16 | {
17 | var handler = GetAllMessageHandlers()
18 | .FirstOrDefault(x => x.Name.ToHandlerName() == messageType);
19 |
20 | return handler;
21 | }
22 |
23 | //reflection utility to cache the known handler types
24 | public static IEnumerable GetAllMessageHandlers()
25 | {
26 | if (_allHandlerTypes != null) return _allHandlerTypes;
27 |
28 | var openGenericType = typeof(IHandleMessages<>);
29 |
30 | _allHandlerTypes = from x in AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
31 | from z in x.GetInterfaces()
32 | let y = x.BaseType
33 | where
34 | ((y != null && y.IsGenericType && openGenericType.IsAssignableFrom(y.GetGenericTypeDefinition()))
35 | || (z.IsGenericType && openGenericType.IsAssignableFrom(z.GetGenericTypeDefinition())))
36 | && !x.IsAbstract && !x.IsInterface
37 | select x;
38 |
39 | return _allHandlerTypes;
40 | }
41 |
42 | //given an input, returns the raw message name when given a handler name
43 | public static string ToHandlerName(this string input)
44 | {
45 | if (input == null)
46 | {
47 | return string.Empty;
48 | }
49 |
50 | return !input.EndsWith(_handlerSuffix) ? input : input.Remove(input.LastIndexOf(_handlerSuffix));
51 | }
52 |
53 | //given an input, returns the raw message name when given a message name
54 | public static string ToMessageName(this string input)
55 | {
56 | if (input == null)
57 | {
58 | return string.Empty;
59 | }
60 |
61 | return !input.EndsWith(_messageSuffix) ? input : input.Remove(input.LastIndexOf(_messageSuffix));
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/DynamicDispatch/Messaging/Messages/BarMessage.cs:
--------------------------------------------------------------------------------
1 | namespace DynamicDispatch.Messaging.Messages
2 | {
3 | //simple POCO with a marker interface
4 | public class BarMessage : IMessage
5 | {
6 | public int Age { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/DynamicDispatch/Messaging/Messages/FooMessage.cs:
--------------------------------------------------------------------------------
1 | namespace DynamicDispatch.Messaging.Messages
2 | {
3 | //simple POCO with a marker interface
4 | public class FooMessage : IMessage
5 | {
6 | public int FooId { get; set; }
7 | public string Name { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/DynamicDispatch/Messaging/Messages/IMessage.cs:
--------------------------------------------------------------------------------
1 | namespace DynamicDispatch.Messaging.Messages
2 | {
3 | public interface IMessage
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/DynamicDispatch/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DynamicDispatch.Messaging.Dispatching;
3 | using DynamicDispatch.Messaging.Messages;
4 |
5 | namespace DynamicDispatch
6 | {
7 | class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 | //this class will 'route' our message to a handler
12 | IDispatchMessages messageDispatcher = new MessageDispatcher();
13 |
14 | //send a FooMessage and get a response from IHandleMessages
15 | //I'm using a convention here, of the message class and the handler class should be named like so:
16 | /*
17 | * Message and Handler
18 | *
19 | * So in the case of a message/handler pair named Foo, you'd create a FooMessage class and a FooHandler class
20 | */
21 |
22 | //the messages are unique and don't need anything other than a marker interface called IMessage
23 | var response = messageDispatcher.Dispatch(new FooMessage
24 | {
25 | FooId = 1234,
26 | Name = "Mr. Foo"
27 | });
28 |
29 | Console.WriteLine(response);
30 |
31 | //this message class is completely different
32 | response = messageDispatcher.Dispatch(new BarMessage()
33 | {
34 | Age = 99
35 | });
36 |
37 | Console.WriteLine(response);
38 |
39 | //once the dispatcher is created, to add new functionality is a matter of adding a new message\handler pair
40 |
41 | Console.ReadKey();
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/DynamicDispatch/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("DynamicDispatch")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("DynamicDispatch")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("44dd3435-713a-43c5-a35e-e5c56373ab75")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/DynamicDispatch/README.md:
--------------------------------------------------------------------------------
1 | # Dynamic Dispatch
2 |
3 | ## Problem it solves
4 | Sometimes you'll want to be able to 'publish something' and it be 'handled' by another class.
5 |
6 | The effect you'd like to create is a 'routed' message; from client to handler.
7 |
8 | Dynamic dispatch allows for a one-time setup of a 'dispatcher' that routes a published POCO to the appropriate handler.
9 |
10 | Subsequent message\handler pairs can be created quickly.
11 |
12 | i.e. `FooMessage` gets published and handled by `IHandleMessages`
13 |
14 | ## Common Uses
15 |
16 | Messaging systems can send\receive messages that get handled by the appropriate handler class.
17 |
18 | This allows for quick and easy publish\handle pattern.
19 |
20 | The example contained can be modified to publish data on one box and the handlers on another box. To do that will require a transport such as a queue or http and the appropriate serialization.
21 |
22 | Combined with threading you can make a high-performance service router.
23 |
--------------------------------------------------------------------------------
/Observer/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Observer/BubbleObserver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Observer
4 | {
5 | public class BubbleObserver
6 | {
7 | public BubbleObserver(ICanBeObserved observableThing)
8 | {
9 | //let's register our event handler
10 | if (observableThing != null)
11 | {
12 | observableThing.OnSomethingAmazingJustHappened += _observableThing_OnSomethingAmazingJustHappened;
13 | }
14 | }
15 |
16 | private void _observableThing_OnSomethingAmazingJustHappened(object sender, SomethingAmazingEventArgs e)
17 | {
18 | Console.WriteLine($"I don't care about Unicorns, but I noticed that something amazing happened and there was a bubble factor of: {e.BubbleFactor}!");
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Observer/ICanBeObserved.cs:
--------------------------------------------------------------------------------
1 | namespace Observer
2 | {
3 | //NOTE: this lives outside of the interface
4 | public delegate void SomethingAmazingJustHappenedHandler(object sender, SomethingAmazingEventArgs e);
5 |
6 | public interface ICanBeObserved
7 | {
8 | //this is the name of the 'broadcasting' method
9 | event SomethingAmazingJustHappenedHandler OnSomethingAmazingJustHappened;
10 |
11 | //this has nothing to do with observables, just regular 'ol biz logic
12 | void DoSomethingAmazing();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Observer/Observer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {E742DE42-717D-4560-87E9-0AECE9DCD6E3}
8 | Exe
9 | Properties
10 | Observer
11 | Observer
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
65 |
--------------------------------------------------------------------------------
/Observer/Program.cs:
--------------------------------------------------------------------------------
1 | namespace Observer
2 | {
3 | class Program
4 | {
5 | static void Main(string[] args)
6 | {
7 | //this class is observable
8 | var unicornDancer = new UnicornDancer();
9 |
10 | //these two classes will do the observing
11 | new BubbleObserver(unicornDancer);
12 | new UnicornObserver(unicornDancer);
13 |
14 | //You could create an observer to send these events to Slack. Only the SlackObserver would need to know about Slack.
15 |
16 | /*
17 | * You may have a ton of other code in between the observer registrations and the actual execution of the observed class
18 | */
19 |
20 | //unicorns will dance, bubbles will be produced and the observers will get a chance to do something with the event arguments broadcast
21 | unicornDancer.DoSomethingAmazing();
22 |
23 | //NOTE: order of the observer execution is driven by the order by which the observers are registered.
24 | //However it is considered bad form to rely on this order.
25 |
26 | //we can do more amazing stuff and the observers will still be listening
27 | unicornDancer.DoSomethingAmazing();
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Observer/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Observer")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Observer")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("e742de42-717d-4560-87e9-0aece9dcd6e3")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Observer/README.md:
--------------------------------------------------------------------------------
1 | # Observer
2 |
3 | ## Problem it solves
4 | Often time a class can get wrapped up into handling things such as logging, performance monitoring or even specific business cases which sully up a class or couple it to something.
5 |
6 | The observer pattern is a great way to decouple specifics from a core\engine.
7 |
8 | ## Common Uses
9 |
10 | The observer pattern can be used to broadcast important steps of an execution to other classes. For instance when a long running process begins\ends, you could broadcast these events and act upon them by updating Slack.
11 |
12 | By doing so the core code (observable) wouldn't have to know anything about Slack, only the observing class would.
13 |
14 | ## Demo
15 |
16 | I've created a video demo of the Observer Pattern that you can have a peek at here: https://www.youtube.com/watch?v=DgGKt0YgIBY&feature=youtu.be
17 |
--------------------------------------------------------------------------------
/Observer/SomethingAmazingEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Observer
4 | {
5 | //NOTE: the inheritance of EventArgs
6 | public class SomethingAmazingEventArgs : EventArgs
7 | {
8 | public int NumberOfUnicornsDancing { get; set; }
9 | public int BubbleFactor { get; set; }
10 |
11 | //create a constructor with anything you want any observers to know about this amazing event
12 | public SomethingAmazingEventArgs(int numberOfUnicornsDancing, int bubbleFactor)
13 | {
14 | NumberOfUnicornsDancing = numberOfUnicornsDancing;
15 | BubbleFactor = bubbleFactor;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Observer/UnicornDancer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Observer
4 | {
5 | public class UnicornDancer : ICanBeObserved
6 | {
7 | private static readonly Random Rand = new Random();
8 |
9 | public event SomethingAmazingJustHappenedHandler OnSomethingAmazingJustHappened;
10 | public void DoSomethingAmazing()
11 | {
12 | Console.WriteLine("Doing something amazing with Unicorns in here, we should broadcast this out!");
13 |
14 | //NOTE: we're using the `?` in case of no observers
15 | //we can pass things to the event args and they will get send to all observers
16 | OnSomethingAmazingJustHappened?.Invoke(this, new SomethingAmazingEventArgs(Rand.Next(100), Rand.Next(10000)));
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Observer/UnicornObserver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Observer
4 | {
5 | public class UnicornObserver
6 | {
7 | public UnicornObserver(ICanBeObserved observableThing)
8 | {
9 | //let's register our event handler
10 | if (observableThing != null)
11 | {
12 | observableThing.OnSomethingAmazingJustHappened += ObservableThing_OnSomethingAmazingJustHappened;
13 | }
14 | }
15 |
16 | private void ObservableThing_OnSomethingAmazingJustHappened(object sender, SomethingAmazingEventArgs e)
17 | {
18 | Console.WriteLine($"I don't care about Bubbles, but I noticed that something amazing happened and there were {e.NumberOfUnicornsDancing} unicorns dancing!");
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Pipeline/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Pipeline/IPipeline.cs:
--------------------------------------------------------------------------------
1 | using Pipeline.PipeModel;
2 | using Pipeline.Pipes;
3 |
4 | namespace Pipeline
5 | {
6 | //an interface that represents the required methods of a pipeline
7 | //the TModel and where are generic and require the model used to be of type IModel
8 | public interface IPipeline where TModel : IModel
9 | {
10 | void Register(IPipe pipe);
11 | void RemoveAt(int index);
12 | void Process(TModel model);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Pipeline/ImagePipeline.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Pipeline.PipeModel;
3 | using Pipeline.Pipes;
4 |
5 | namespace Pipeline
6 | {
7 | //implementation of a pipeline
8 | public class ImagePipeline : IPipeline
9 | {
10 | //we'll store the registered actions here
11 | private readonly List> _pipeline = new List>();
12 |
13 | public void Register(IPipe pipe)
14 | {
15 | _pipeline.Add(pipe);
16 | }
17 |
18 | public void RemoveAt(int index)
19 | {
20 | _pipeline.RemoveAt(index);
21 | }
22 |
23 | //just a simple loop that calls process on each pipe segment
24 | public void Process(ImageModel model)
25 | {
26 | foreach (var pipe in _pipeline)
27 | {
28 | pipe.Process(model);
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Pipeline/PipeModel/IModel.cs:
--------------------------------------------------------------------------------
1 | namespace Pipeline.PipeModel
2 | {
3 | //a simple model that must be used in the pipeline
4 | //adjust this to suit your needs
5 | public interface IModel
6 | {
7 | string ProcessLog { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Pipeline/PipeModel/ImageModel.cs:
--------------------------------------------------------------------------------
1 | namespace Pipeline.PipeModel
2 | {
3 | //a token model wrapper that implements IModel
4 | public class ImageModel : IModel
5 | {
6 | public string ProcessLog { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/Pipeline/Pipeline.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {91D2F6F9-7E2B-44D9-A6B8-12626B786139}
8 | Exe
9 | Properties
10 | Pipeline
11 | Pipeline
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
68 |
--------------------------------------------------------------------------------
/Pipeline/Pipes/CropAction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Pipeline.PipeModel;
3 |
4 | namespace Pipeline.Pipes
5 | {
6 | //a 'pipe' is just a very focused action, this one represents 'cropping'
7 | //the real 'crop' code would obviously be different, but the plumbing would remain the same
8 | public class CropAction : IPipe
9 | {
10 | public ImageModel Process(ImageModel model)
11 | {
12 | Console.WriteLine("Cropping!");
13 |
14 | model.ProcessLog += "==>Cropping!";
15 |
16 | return model;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Pipeline/Pipes/DesaturateAction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Pipeline.PipeModel;
3 |
4 | namespace Pipeline.Pipes
5 | {
6 | public class DesaturateAction : IPipe
7 | {
8 | public ImageModel Process(ImageModel model)
9 | {
10 | Console.WriteLine("Desaturating!");
11 |
12 | model.ProcessLog += "==>Desaturating!";
13 |
14 | return model;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Pipeline/Pipes/IPipe.cs:
--------------------------------------------------------------------------------
1 | using Pipeline.PipeModel;
2 |
3 | namespace Pipeline.Pipes
4 | {
5 | //an interface that represents a 'pipe' action
6 | //i've constrained the TModel to be of type IModel
7 | public interface IPipe where TModel : IModel
8 | {
9 | //what comes in will be altered and then returned as output
10 | TModel Process(TModel input);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Pipeline/Pipes/RotateAction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Pipeline.PipeModel;
3 |
4 | namespace Pipeline.Pipes
5 | {
6 | public class RotateAction : IPipe
7 | {
8 | public ImageModel Process(ImageModel model)
9 | {
10 | Console.WriteLine("Rotating!");
11 |
12 | model.ProcessLog += "==>Rotating!";
13 |
14 | return model;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Pipeline/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Pipeline.PipeModel;
3 | using Pipeline.Pipes;
4 |
5 | namespace Pipeline
6 | {
7 | class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 | //create a new pipeline
12 | var pipeLine = new ImagePipeline();
13 | //create a simple object to represent an image
14 | //the real image manipulation details would be much more involved
15 | var image = new ImageModel();
16 |
17 | //add the actions that the pipeline should do in a particular order
18 | pipeLine.Register(new CropAction());
19 | pipeLine.Register(new DesaturateAction());
20 | pipeLine.Register(new RotateAction());
21 |
22 | //process the image based on the the above
23 | pipeLine.Process(image);
24 |
25 | //let's see what steps actually were taken
26 | Console.WriteLine(image.ProcessLog);
27 |
28 | //let's remove the desaturate action
29 | pipeLine.RemoveAt(1);
30 | //we'll add a rotation
31 | pipeLine.Register(new RotateAction());
32 | //let's reprocess the image
33 | pipeLine.Process(image);
34 |
35 | //let's see what steps ran
36 | Console.WriteLine(image.ProcessLog);
37 |
38 | Console.ReadKey();
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Pipeline/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Pipeline")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Pipeline")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("91d2f6f9-7e2b-44d9-a6b8-12626b786139")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Pipeline/README.md:
--------------------------------------------------------------------------------
1 | # Pipeline
2 |
3 | ## Problem it solves
4 | When an object needs to be processed conditionally based on a series of steps, the Pipeline pattern separates each step in the process into discrete classes.
5 |
6 | The pipeline is just a series of ordered steps that can be adjusted to suit custom needs without having a large class that tries to handle all situations.
7 |
8 | ## Common Uses
9 | Image processing makes use of pipelines quite often. You may need to first crop, adjust color and then save an image. An e-commerce system (like [uCommerce](https://ucommerce.net)) uses pipelines to allow the default behavior of a checkout process to be altered as needed by the developer.
10 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/Bar.cs:
--------------------------------------------------------------------------------
1 | namespace PoorMansDependencyInjection
2 | {
3 | class Bar : IBar
4 | {
5 | public void DoSomething()
6 | {
7 |
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/Bar2.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace PoorMansDependencyInjection
4 | {
5 | class Bar2 : IBar
6 | {
7 | public void DoSomething()
8 | {
9 |
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/BestFoo.cs:
--------------------------------------------------------------------------------
1 | namespace PoorMansDependencyInjection
2 | {
3 | class BestFoo : IFoo
4 | {
5 | private readonly IBar _bar;
6 |
7 | public BestFoo()
8 | : this(new Bar())
9 | {
10 | }
11 |
12 | public BestFoo(IBar bar)
13 | {
14 | _bar = bar;
15 | }
16 |
17 | public void Blah()
18 | {
19 | //I'm using bar, but I can swap it out easily without this line of code caring
20 | _bar.DoSomething();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/BetterFoo.cs:
--------------------------------------------------------------------------------
1 | namespace PoorMansDependencyInjection
2 | {
3 | class BetterFoo : IFoo
4 | {
5 | private readonly IBar _bar;
6 |
7 | public BetterFoo(IBar bar)
8 | {
9 | _bar = bar;
10 | }
11 |
12 | public void Blah()
13 | {
14 | //I'm using bar, but I can swap it out easily without this line of code caring
15 | _bar.DoSomething();
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/BetterFoo2.cs:
--------------------------------------------------------------------------------
1 | namespace PoorMansDependencyInjection
2 | {
3 | class BetterFoo2 : IFoo
4 | {
5 | private IBar _bar;
6 |
7 | public IBar Bar
8 | {
9 | //if I don't have a IBar impl, use a default one
10 | get { return _bar ?? (_bar = new Bar()); }
11 |
12 | set { _bar = value; }
13 | }
14 |
15 | public void Blah()
16 | {
17 | _bar.DoSomething();
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/Foo.cs:
--------------------------------------------------------------------------------
1 | namespace PoorMansDependencyInjection
2 | {
3 | public class Foo : IFoo
4 | {
5 | public void Blah()
6 | {
7 | //this will be near impossible to swap out
8 | var bar = new Bar();
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/IBar.cs:
--------------------------------------------------------------------------------
1 | namespace PoorMansDependencyInjection
2 | {
3 | interface IBar
4 | {
5 | void DoSomething();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/IFoo.cs:
--------------------------------------------------------------------------------
1 | namespace PoorMansDependencyInjection
2 | {
3 | interface IFoo
4 | {
5 | void Blah();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/PoorMansDependencyInjection.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {08D03EC8-6693-4A80-BC65-E685B2ABA75B}
8 | Exe
9 | Properties
10 | PoorMansDependencyInjection
11 | PoorMansDependencyInjection
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
68 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/Program.cs:
--------------------------------------------------------------------------------
1 | namespace PoorMansDependencyInjection
2 | {
3 | class Program
4 | {
5 | static void Main(string[] args)
6 | {
7 | /*
8 | * Poor man's DI is just a way to keep your code SOLID without the use of a DI container
9 | */
10 |
11 | //for instance a typical developer might write a class like Foo where it's dependencies are trapped in the body of a method
12 | var foo = new Foo();
13 | foo.Blah();
14 |
15 | //in general, that may be good enough but if you inspect class Foo, you'll see that it needs class Bar. At test time
16 | //you'll have a difficult time mocking or switching off that code without doing some pretty strange configuration setups
17 |
18 | //meet class BetterFoo, it takes a dependency via the constructor
19 | var betterFoo = new BetterFoo(new Bar());
20 | betterFoo.Blah();
21 |
22 | //meet yet another twist on it by meeting class BetterFoo2. This class has a default use of IBar and doesn't need to be passed
23 | //explicitly AND it can be swapped out
24 | var betterFoo2 = new BetterFoo2();
25 | betterFoo2.Blah();
26 |
27 | //as-is, betterFoo2 will use Bar as the IBar impl, however if I want to use Bar2 instead, I can do the following
28 | betterFoo2.Bar = new Bar2();
29 | betterFoo2.Blah();
30 |
31 | //as my buddy @bleedo (on Twitter) pointed out, you can have yet another way to have a better foo. BestFoo has a default constructor
32 | //with the ability to pass dependencies via an overloaded constructor
33 | var bestFoo = new BestFoo();
34 | bestFoo.Blah();
35 |
36 | bestFoo = new BestFoo(new Bar2());
37 | bestFoo.Blah();
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("PoorMansDependencyInjection")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("PoorMansDependencyInjection")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("08d03ec8-6693-4a80-bc65-e685b2aba75b")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/PoorMansDependencyInjection/README.md:
--------------------------------------------------------------------------------
1 | # Poor Man's Dependency Injection
2 |
3 | ## Problem it solves
4 | This pattern allows for depenencies to be easily changed for unit testing without the need for magical things like IoC containers.
5 |
6 | ## Common Uses
7 | Rather than `new` a dependency within a constructor or method in a class, all dependencies are taken via the contructor or a default is provided with a public getter.
8 |
9 | This is most useful when performing unit testing so you can easily return happy signals on irrelevant dependencies (things you didn't want to test anyway).
10 |
11 | Checkout a video I made on Poor man's DI and IoC containers: https://www.youtube.com/watch?v=xR0LotJQzlk
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # c-sharp-patterns
2 |
3 | This is yet another repo dedicated to design patterns in C#. This e-book will focus on the Gang of Four (https://en.wikipedia.org/wiki/Design_Patterns) patterns and other things such as SOLID, Dependency Injection and composition over inheritance.
4 |
5 | ## Contributors
6 |
7 | Anyone can contribute by sending a PR.
8 |
9 |
10 | ## Disclaimer
11 |
12 | As with many things in programming, pattern implementations can be pretty subjective. The main takeway is that each pattern solves a particular pain point commonly found in modern object-oriented programming.
13 |
--------------------------------------------------------------------------------
/SimpleFactory/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/SimpleFactory/Bar.cs:
--------------------------------------------------------------------------------
1 | namespace SimpleFactory
2 | {
3 | class Bar
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SimpleFactory/Program.cs:
--------------------------------------------------------------------------------
1 | namespace SimpleFactory
2 | {
3 | class Program
4 | {
5 | static void Main(string[] args)
6 | {
7 | //traditionally objects are created with the 'new' operator
8 | var foo = new Bar();
9 |
10 | //over time if you change the constructor, you'll have to hunt\peck all over to update the signature in many places
11 |
12 | //a simple factory simply is a method that return an instance of an object thus allowing one place to make any changes
13 | foo = Create();
14 | }
15 |
16 | static Bar Create()
17 | {
18 | return new Bar();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SimpleFactory/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("SimpleFactory")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("SimpleFactory")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("d1953d7f-f0af-46d1-8171-b44f80d7832e")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/SimpleFactory/README.md:
--------------------------------------------------------------------------------
1 | # Simple Factory
2 |
3 | ## Problem it solves
4 | Often times code will have many places where the `new` operator is used to create a new class. However over time if the implementation needs altered (signature update) or completely swapped out, this can become cumbersome. The simple factory pattern allows to centralize the creation of a class.
5 |
6 | ## Common Uses
7 | The most common use is to create a class with a single method named `.Create()`. This method does one thing, it creates another object and any class who needs a new object it provides will delegate the task to the method. This greatly increases future maintainability.
8 |
9 | A better factory can be found by using the [Abstract Factory](../AbstractFactory/README.md).
10 |
11 | Check out a video I made on factories: https://www.youtube.com/watch?v=qzxp9p7UP_Y
12 |
--------------------------------------------------------------------------------
/SimpleFactory/SimpleFactory.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {D1953D7F-F0AF-46D1-8171-B44F80D7832E}
8 | Exe
9 | Properties
10 | SimpleFactory
11 | SimpleFactory
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
61 |
--------------------------------------------------------------------------------
/Singleton/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Singleton/NonThreadSafeSingleton.cs:
--------------------------------------------------------------------------------
1 | namespace Singleton
2 | {
3 | public class NonThreadSafeSingleton
4 | {
5 | //holds our one and only instance
6 | private static NonThreadSafeSingleton _nonThreadSafeSingleton;
7 |
8 | //private constructor to disallow anyone 'new'ing one up
9 | private NonThreadSafeSingleton()
10 | {
11 |
12 | }
13 |
14 | //add as many properties as you want
15 | public string PropertyOne { get; set; }
16 |
17 | //the 'instance' property initializes our private instance or returns if already initialized
18 | public static NonThreadSafeSingleton Instance => _nonThreadSafeSingleton ?? (_nonThreadSafeSingleton = new NonThreadSafeSingleton());
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Singleton/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Singleton
4 | {
5 | class Program
6 | {
7 | static void Main(string[] args)
8 | {
9 | //many times we should use a thread safe implementation
10 | Console.WriteLine(ThreadSafeSingleton.Instance.PropertyOne); //hello world
11 |
12 | Console.WriteLine(ThreadSafeSingleton.Instance.PropertyOne); //hello world
13 |
14 | ThreadSafeSingleton.Instance.Clear(); //will require a re-initialization on the next usage
15 |
16 | Console.WriteLine(ThreadSafeSingleton.Instance.PropertyOne); //hello world
17 |
18 | //we can also use a non-thread safe singleton if you need to but you'll wanna understand when this is ok
19 | NonThreadSafeSingleton.Instance.PropertyOne = "Foo";
20 |
21 | Console.WriteLine(NonThreadSafeSingleton.Instance.PropertyOne);
22 |
23 | Console.ReadKey();
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Singleton/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("c-sharp-patterns")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("University of Notre Dame")]
12 | [assembly: AssemblyProduct("c-sharp-patterns")]
13 | [assembly: AssemblyCopyright("Copyright © University of Notre Dame 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("c995f399-fa01-4d04-b235-2590f54479af")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Singleton/README.md:
--------------------------------------------------------------------------------
1 | # Singleton
2 |
3 | ## Problem it solves
4 | Often times you will want one global instance of an object instead of using `static` objects. Instance objects can utilize extension methods whereas a purely static class cannot.
5 |
6 | ## Common Uses
7 | The Singleton pattern is a good option to use when you'd like to restrict the creation of an object to only one instance.
8 |
9 | A common use may be to use a global object to work as a service object.
10 |
11 | There are both thread-safe and non-thread-safe versions to choose from. The main thing to look out for when using non-thread-safe Singletons is during intialization. If your startup code has more than one thread that could invoke your class, you will need to use the thread-safe version. However if you're startup code is single-threaded; you can create a non-thread-safe version (less code). Keep in mind that thread-safety is its own topic and if you manage state in your Singleton, you'll have other things to consider.
12 |
13 | A video for you to watch if you so desire: https://www.youtube.com/watch?v=XZl3FgkYazI
14 |
--------------------------------------------------------------------------------
/Singleton/Singleton.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {F97FEF4A-DE8C-452C-9535-08CA5E977011}
8 | Exe
9 | Properties
10 | Singleton
11 | Singleton
12 | v4.5
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
62 |
--------------------------------------------------------------------------------
/Singleton/ThreadSafeSingleton.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Singleton
4 | {
5 | public class ThreadSafeSingleton
6 | {
7 | //this will hold the internal instance of our singleton
8 | private static volatile ThreadSafeSingleton _instance;
9 |
10 | //this is used purely for thread safety
11 | private static readonly object _padLock = new object();
12 |
13 | //you can create as many properties as you'd like
14 | public string PropertyOne;
15 |
16 | //note that the constructor is private and only the class itself can create a new instance
17 | private ThreadSafeSingleton()
18 | {
19 | }
20 |
21 | //this is where the instance will live, notice the return value is the singleton class itself
22 | public static ThreadSafeSingleton Instance
23 | {
24 | get
25 | {
26 | //using double check locking to make this thread-safe
27 | //if already initialized, it'll just return the already created instance
28 | if (_instance == null)
29 | {
30 | lock (_padLock)
31 | {
32 | if (_instance == null)
33 | {
34 | //this will only run if the singleton has never been initialized or has been set to null
35 | //otherwise this code will never run after the first time
36 | Console.WriteLine("Initializing singleton...");
37 |
38 | //create an instance and set it to your private member
39 | _instance = new ThreadSafeSingleton();
40 |
41 | //set the values of any properties you have
42 | _instance.PropertyOne = "Hello World!";
43 | }
44 | }
45 | }
46 |
47 | return _instance;
48 | }
49 | }
50 |
51 | //a helper method designed to set the singleton to null which triggers a rebuilding of the instance on next use
52 | public void Clear()
53 | {
54 | Console.WriteLine("Clearing singleton...");
55 |
56 | //by setting this to null, the next thread that uses this singleton will cause it to reinitialize
57 | _instance = null;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Solid/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Solid/EmailDependency.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Solid
4 | {
5 | public class EmailDependency :IEmailDependency
6 | {
7 | public void SendMail()
8 | {
9 | Console.WriteLine("Sending an email...");
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Solid/IEmailDependency.cs:
--------------------------------------------------------------------------------
1 | namespace Solid
2 | {
3 | public interface IEmailDependency
4 | {
5 | void SendMail();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Solid/ISolidEmailer.cs:
--------------------------------------------------------------------------------
1 | namespace Solid
2 | {
3 | public interface ISolidEmailer
4 | {
5 | void SendMail();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Solid/ISolidUserRecordUpdater.cs:
--------------------------------------------------------------------------------
1 | namespace Solid
2 | {
3 | public interface ISolidUserRecordUpdater
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Solid/IUpdateUserRecords.cs:
--------------------------------------------------------------------------------
1 | namespace Solid
2 | {
3 | public interface IUpdateUserRecords
4 | {
5 | void DoSomeUpdates();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Solid/NotSolid.cs:
--------------------------------------------------------------------------------
1 | namespace Solid
2 | {
3 | public class NotSolid
4 | {
5 | //This class violates 'S', 'O', 'L' and 'D'
6 | //'S' because it does two seemingly unrelated things, emailing and updating user records
7 | public NotSolid()
8 | {
9 |
10 | }
11 |
12 | public void DoSomething()
13 | {
14 | //this will work, however it 'traps' this dependency in this method
15 | //there is no way to swap this for testing
16 | //Violates 'O', 'D' and 'L'
17 | //'L' and 'D' because we depend on a concrete class
18 | //'O' because there is no way to change the email dependency
19 | var emailDependency = new EmailDependency();
20 |
21 | emailDependency.SendMail();
22 | }
23 |
24 | public void DoSomethingElse()
25 | {
26 | //also trapping a dependency here
27 | //seems to be doing something unrelated to sending emails
28 | var update = new UpdateUserRecords();
29 | update.DoSomeUpdates();
30 | }
31 |
32 | //Violates L and D
33 | //Both because we're not using and interface
34 | public void DoSomethingElseAgain(UpdateUserRecords update)
35 | {
36 | update.DoSomeUpdates();
37 | var emailDependency = new EmailDependency();
38 |
39 | emailDependency.SendMail();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Solid/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Solid
4 | {
5 | class Program
6 | {
7 | static void Main(string[] args)
8 | {
9 | //SOLID programming is a way to keep your code maintainable
10 | /*
11 | * It is an acronym and the Wikipedia definition can be found here: https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)
12 | *
13 | * S - Single Responsibility. This one is the most subjective of the definitions. However you should favor smaller classes
14 | * that do specific things as opposed to large classes that do too much.
15 | * O - Open\Closed Principle. Classes should be 'open' for extension and 'closed' for modification. Again more subjectivity
16 | * but if a class doesn't need to be altered and offers extension points; it adheres better to this principle than a class
17 | * that needs to be updated often.
18 | * L - Liskov Substitution Principle. This is more about polymorphism and abstraction. You should be using interfaces and\or
19 | * abstract classes when depending on another class. Therefore when you want to change out a dependency, the contract (interface)
20 | * allows you to.
21 | * I - Interface Segregation Principle - This sort of blends the 'O' and the 'I' with a little bit of 'S'. Favor smaller specific
22 | * purpose interfaces to large interfaces that do too much. There is no example here, but just think of a giant abstraction that does
23 | * too much.
24 | * D - Dependency Inversion Principle - Depend on abstractions, not concrete classes. More blending of the above, but dependencies
25 | * should be pass thru a constructor or property as an abstraction and not a concrete class. Also do not 'trap' a dependency.
26 | *
27 | *
28 | * ** Remember, these are guidelines, not hard-fast rules. Feel free to make exceptions as needed. **
29 | */
30 |
31 | var notSolid = new NotSolid();
32 |
33 | notSolid.DoSomething();
34 | notSolid.DoSomethingElse();
35 |
36 | //we take our dependencies via the constructor, view the definition to notice we are depending on an interface and not a concrete mailer
37 | var solidEmailer = new SolidEmailer(new EmailDependency());
38 | solidEmailer.SendMail();
39 |
40 | //same as above, but we split the user record updates from the emails
41 | //we take the email dependency as a dependency
42 | var solidUpdater = new SolidUserRecordUpdater(new UpdateUserRecords(), new EmailDependency());
43 | solidUpdater.DoSomethingElse();
44 | solidUpdater.DoSomethingElseAgain();
45 |
46 | Console.ReadKey();
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Solid/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Solid")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Solid")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("d0500f3b-e9f2-4534-ae9a-980cc3f0c9a4")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Solid/README.md:
--------------------------------------------------------------------------------
1 | # SOLID
2 |
3 | ## Problem it solves
4 | SOLID is a set of principles that help to keep your code maintainable. It's best to know the rules so when you want to break them, you know the repercussions.
5 |
6 | https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)
7 |
8 | ## Common Uses
9 | Many developers use these principles to seperate out dependencies and to guide how to decompose large blocks of logic into modular components.
10 |
--------------------------------------------------------------------------------
/Solid/Solid.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {D0500F3B-E9F2-4534-AE9A-980CC3F0C9A4}
8 | Exe
9 | Properties
10 | Solid
11 | Solid
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
69 |
--------------------------------------------------------------------------------
/Solid/SolidEmailer.cs:
--------------------------------------------------------------------------------
1 | namespace Solid
2 | {
3 | public class SolidEmailer :ISolidEmailer
4 | {
5 | private readonly IEmailDependency _emailDependency;
6 |
7 | //we take our dependencies via the constructor and they are of an abstracted type
8 | //swapping which emailer we use is simple
9 | public SolidEmailer(IEmailDependency emailDependency)
10 | {
11 | _emailDependency = emailDependency;
12 | }
13 |
14 | //we limit this class to only handle emailing
15 | public void SendMail()
16 | {
17 | _emailDependency.SendMail();
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Solid/SolidUserRecordUpdater.cs:
--------------------------------------------------------------------------------
1 | namespace Solid
2 | {
3 | public class SolidUserRecordUpdater : ISolidUserRecordUpdater
4 | {
5 | private readonly IUpdateUserRecords _updater;
6 | private readonly IEmailDependency _emailDependency;
7 |
8 | public SolidUserRecordUpdater(IUpdateUserRecords updater, IEmailDependency emailDependency)
9 | {
10 | _updater = updater;
11 | _emailDependency = emailDependency;
12 | }
13 |
14 | public void DoSomethingElse()
15 | {
16 | _updater.DoSomeUpdates();
17 | }
18 |
19 | public void DoSomethingElseAgain()
20 | {
21 | _updater.DoSomeUpdates();
22 | _emailDependency.SendMail();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Solid/UpdateUserRecords.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Solid
4 | {
5 | public class UpdateUserRecords : IUpdateUserRecords
6 | {
7 | public void DoSomeUpdates()
8 | {
9 | Console.WriteLine("Updating records...");
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Strategy/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Strategy/BadGuy.cs:
--------------------------------------------------------------------------------
1 | using Strategy.MovementStrategies;
2 |
3 | namespace Strategy
4 | {
5 | public class BadGuy : IBadGuy
6 | {
7 | //this is our bad guy
8 | public string Name { get; set; }
9 | public IMovementStrategy MovementStrategy { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Strategy/BadGuyFactory.cs:
--------------------------------------------------------------------------------
1 | using Strategy.MovementStrategies;
2 |
3 | namespace Strategy
4 | {
5 | public static class BadGuyFactory
6 | {
7 | public static IBadGuy Create(string name)
8 | {
9 | //default for guys like Fred
10 | IMovementStrategy movementStrategy = new SideToSideStrategy();
11 |
12 | //Dave is special, so let's assign him up and down
13 | if (name == "Dave")
14 | {
15 | movementStrategy = new UpAndDownStrategy();
16 | }
17 |
18 | //let's construct our bad guys now that our movement strategy has been selected
19 | return new BadGuy
20 | {
21 | Name = name,
22 | MovementStrategy = movementStrategy
23 | };
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Strategy/Henchman.cs:
--------------------------------------------------------------------------------
1 | namespace Strategy
2 | {
3 | public class Henchman
4 | {
5 | public string Name { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Strategy/IBadGuy.cs:
--------------------------------------------------------------------------------
1 | using Strategy.MovementStrategies;
2 |
3 | namespace Strategy
4 | {
5 | //our bad guys abstracted
6 | public interface IBadGuy
7 | {
8 | string Name { get; set; }
9 | IMovementStrategy MovementStrategy { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Strategy/MovementStrategies/IMovementStrategy.cs:
--------------------------------------------------------------------------------
1 | namespace Strategy.MovementStrategies
2 | {
3 | //this interface is used as an abstraction to our strategies
4 | public interface IMovementStrategy
5 | {
6 | void Move();
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/Strategy/MovementStrategies/SideToSideStrategy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Strategy.MovementStrategies
4 | {
5 | //a concrete movement strategy
6 | public class SideToSideStrategy : IMovementStrategy
7 | {
8 | //in an actual game, you can implement the details of how to move side to side
9 | public void Move()
10 | {
11 | Console.WriteLine("I'm moving side to side!");
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Strategy/MovementStrategies/UpAndDownStrategy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Strategy.MovementStrategies
4 | {
5 | //a concrete movement strategy
6 | public class UpAndDownStrategy : IMovementStrategy
7 | {
8 | //in an actual game, you can implement the details of how to move up and down
9 | public void Move()
10 | {
11 | Console.WriteLine("I'm moving up and down.");
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Strategy/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Strategy
4 | {
5 | class Program
6 | {
7 | static void Main(string[] args)
8 | {
9 | //for this example we are creating two henchmen and bad guys that will appear in a video game
10 | //each henchmen\bad guy normally moves side to side, but for henchmen\bad guys named 'Dave', they get to move up and down.
11 |
12 | //henchmen do not use a strategy pattern
13 | var daveHenchman = new Henchman
14 | {
15 | Name = "Dave"
16 | };
17 |
18 | var fredHenchman = new Henchman
19 | {
20 | Name = "Fred"
21 | };
22 |
23 | //we have to do a more explicit move for our henchmen
24 | _move(daveHenchman);
25 | _move(fredHenchman);
26 |
27 | //bad guys do use a strategy pattern
28 | //this example is also using a factory to create our bad guys
29 | //inside the factory you'll see that each bad guy is created and a movement strategy is selected
30 |
31 | //the strategy pattern is encapsulating a behavior in a call class through a concrete implementation of an abstract interface or class
32 |
33 | //the 'how' to move is encapsulated in to discrete classes
34 |
35 | //dave will move up and down
36 | var daveBadGuy = BadGuyFactory.Create("Dave");
37 |
38 | daveBadGuy.MovementStrategy.Move();
39 |
40 | //fred will move side to side
41 | var fredBadGuy = BadGuyFactory.Create("Fred");
42 |
43 | fredBadGuy.MovementStrategy.Move();
44 |
45 | Console.ReadKey();
46 | }
47 |
48 | //this tangles up both up and down and side to side movement
49 | //the strategy pattern helps us make a class per movement type
50 | //the details of the movement are not encapsulated here
51 | //this will lead to a large class with a bunch of logic conditionals
52 | private static void _move(Henchman henchman)
53 | {
54 | if (henchman.Name == "Dave")
55 | {
56 | Console.WriteLine("Moving Up and down!");
57 | }
58 | else
59 | {
60 | Console.WriteLine("Moving side to side!");
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Strategy/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Strategy")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Strategy")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("202078d6-bf8c-4d11-931e-a6a7bfb98b1f")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Strategy/README.md:
--------------------------------------------------------------------------------
1 | # Strategy
2 |
3 | ## Problem it solves
4 | Often times we will write a class then ends up having quite a bit of conditional logic that handles many behaviors via if\then\else\switch.
5 | This leads to having classes that are long with trapped logic. The strategy pattern allows us to encapsulate each behavior as a discrete class that we can assign to another which makes it easier to read and the code becomes portable.
6 |
7 | ## Common Uses
8 | When more than one distinct behavior is possible based on some sort of condition, it is much cleaner to refactor the behaviors into their own classes.
9 | In the video game world, you may want Goomba's to react a certain way to a character while having Bowser react another. Rather than put all of that into a single class, you can put each behvavior into its own class.
10 |
11 | In finance, you may want checking accounts to handle overdrawn accounts differently based upon their account type. For example, a free checking account may have a fee incurred immediately while a premium account may have forgiveness up to a point.
12 | Each behavior can be handled separately without having a mess of if\then\else\switch statements.
13 |
--------------------------------------------------------------------------------
/Strategy/Strategy.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {202078D6-BF8C-4D11-931E-A6A7BFB98B1F}
8 | Exe
9 | Properties
10 | Strategy
11 | Strategy
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
67 |
--------------------------------------------------------------------------------
/Tasks/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Tasks/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Tasks.Work;
4 |
5 | namespace Tasks
6 | {
7 | class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 | var work = new DoSomething();
12 |
13 | //immediately runs in a separate thread
14 | var process1Task = Task.Run(() => work.Process1());
15 |
16 | //immediately runs in a separate thread
17 | var process2Task = Task.FromResult(work.Process2());
18 |
19 | //blocks until it's done due to the 'Result' keyword
20 | Console.WriteLine($"Status: {process2Task.Result}");
21 |
22 | //let's kick off process3 immediately
23 | var process3Task = _process3Async(work);
24 |
25 | //and wait for it (meanwhile process 1 is still going likely)
26 | var process3TaskResult = process3Task.Result;
27 |
28 | Console.WriteLine($"Process3: {process3TaskResult}");
29 |
30 | //if we don't wait, the main thread will exit before Process1 is complete.
31 | //we'll never risk this with Process2 since we block until it's done
32 | process1Task.Wait();
33 |
34 | Console.WriteLine("All tasks complete!");
35 |
36 | Console.ReadKey();
37 |
38 | //now let's run three tasks but wait for them all to complete
39 | //they will end in reverse order
40 | Task.WaitAll(new Task[]
41 | {
42 | work.Process3Async(3000),
43 | work.Process3Async(2000),
44 | work.Process3Async(1000)
45 | });
46 |
47 | Console.WriteLine("All tasks complete!");
48 |
49 | Console.ReadKey();
50 |
51 | //now let's run three tasks but wait for ONLY ONE of them to complete
52 | //they will end in reverse order
53 | Task.WaitAny(new Task[]
54 | {
55 | work.Process3Async(3000),
56 | work.Process3Async(2000),
57 | work.Process3Async(1000)
58 | });
59 |
60 | Console.WriteLine("One task complete!");
61 |
62 | Console.ReadKey();
63 | }
64 |
65 | //you can define an async task by using the 'async' keyword, the 'Task` return type and the 'await keyword on something
66 | //defined as a task
67 | private static async Task _process3Async(DoSomething work)
68 | {
69 | return await work.Process3Async();
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Tasks/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Tasks")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Tasks")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("6262a23d-0e46-4492-8b98-105bce96a8ac")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Tasks/Tasks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {6262A23D-0E46-4492-8B98-105BCE96A8AC}
8 | Exe
9 | Properties
10 | Tasks
11 | Tasks
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
61 |
--------------------------------------------------------------------------------
/Tasks/Work/DoSomething.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace Tasks.Work
6 | {
7 | public class DoSomething
8 | {
9 | public void Process1()
10 | {
11 | Console.WriteLine("Doing Process1....");
12 |
13 | Thread.Sleep(10000);
14 |
15 | Console.WriteLine("Process1 Done.");
16 | }
17 |
18 | public string Process2()
19 | {
20 | Console.WriteLine("Doing Process2....");
21 |
22 | Thread.Sleep(1000);
23 |
24 | Console.WriteLine("Process2 Done.");
25 |
26 | return "All done.";
27 | }
28 |
29 | public async Task Process3Async(int sleepInMs = 5000)
30 | {
31 | //should run immediately
32 | Console.WriteLine($"Doing Process3 ({sleepInMs})...");
33 |
34 | //simulates a blocking task
35 | await Task.Run(() =>
36 | {
37 | Thread.Sleep(sleepInMs);
38 | });
39 |
40 | //this should run afterwards
41 | Console.WriteLine($"Process3 ({sleepInMs}) Done.");
42 |
43 | return "All done.";
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Threading/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Threading/IManageThreads.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Threading
4 | {
5 | public interface IManageThreads
6 | {
7 | void Queue(object o);
8 | void ProcessQueue(Action