Abstract factory
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
Abstract Factory Design Pattern goal is to assemble classes or products that you need for the software.
The abstract factory design pattern is merely an extension of the factory method pattern, which allows you to create objects without being concerned about the actual class of the objects being produced. The abstract factory pattern extends the factory method pattern by allowing more types of objects to be produced.
Let's start with an example first, then we will see how it eventually leads us to the abstract factory pattern.
Starting from the factory method example where we had online bookstores that can choose different book distributors to ship the books to the customers:
In the example, both BookStoreA and BookStoreB choose which distributor (EastCoastDistributor or MidWestDistributor or WestCoastDistributor) to use based on the location of the customer. This logic is in each bookstore's GetDistributor method.
We can extend this factory method pattern to the abstract factory pattern by:
Adding another product that the factories can produce. In this example, we will add Advertisers that help the bookstores advertise their stores online.
Each bookstore can then choose their own distributors and advertisers inside their own GetDistributor and GetAdvertiser method.
After this extension, we now have:
Both bookstores (the factories) have their own GetAdvertiser method that chooses which advertiser to produce, as well as their own GetDistributor method that chooses which distributor to produce.
This allows you to have client code (calling code) such as:
public void Advertise(IBookStore s) { IAdverister a = s.GetAdvertiser(); a.Advertise(); }
Notice that regardless if you pass in BookStoreA or BookStoreB into the method above, this client code does not need to be changed since it will get the correct advertiser automatically using the internal logics within the factories. It is the factories (BookStoreA and BookStoreB) that determines which advertiser to produce. The same goes for choosing which book distributor to produce.
Below is the UML of the Abstract Factory design pattern, which is what we have in our example:
The benefit of the Abstract Factory pattern is that it allows you to create a groups of products (the distributors and the advertisers) without having to know the actual class of the product being produced. The result is that you can have client code that does not need to be changed when the internal logic of the factories on which product to produce changed.
Below are the implementation code and the output of the Abstract Factory pattern using our example. Notice that you can change the types of the products (the distributors and the advertisers) being produced by changing the code in the factories (the bookstores) without changing the client code:
public enum CustomerLocation { EastCoast, WestCoast } class Program { static void Main(string[] args) { IBookStore storeA = new BookStoreA(CustomerLocation.EastCoast); Console.WriteLine("Book Store A with a customer from East Coast:"); ShipBook(storeA); Advertise(storeA); IBookStore storeB = new BookStoreB(CustomerLocation.WestCoast); Console.WriteLine("Book Store B with a customer from West Coast:"); ShipBook(storeB); Advertise(storeB); } //**** client code that does not need to be changed *** private static void ShipBook(IBookStore s) { IDistributor d = s.GetDistributor(); d.ShipBook(); } //**** client code that does not need to be changed *** private static void Advertise(IBookStore s) { IAdvertiser a = s.GetAdvertiser(); a.Advertise(); } } //the factory public interface IBookStore { IDistributor GetDistributor(); IAdvertiser GetAdvertiser(); } //concrete factory public class BookStoreA : IBookStore { private CustomerLocation location; public BookStoreA(CustomerLocation location) { this.location = location; } IDistributor IBookStore.GetDistributor() { //internal logic on which distributor to return //*** logic can be changed without changing the client code **** switch (location) { case CustomerLocation.EastCoast: return new EastCoastDistributor(); case CustomerLocation.WestCoast: return new WestCoastDistributor(); } return null; } IAdvertiser IBookStore.GetAdvertiser() { //internal logic on which distributor to return //*** logic can be changed without changing the client code **** switch (location) { case CustomerLocation.EastCoast: return new RedAdvertiser(); case CustomerLocation.WestCoast: return new BlueAdvertiser(); } return null; } } //concrete factory public class BookStoreB : IBookStore { private CustomerLocation location; public BookStoreB(CustomerLocation location) { this.location = location; } IDistributor IBookStore.GetDistributor() { //internal logic on which distributor to return //*** logic can be changed without changing the client code **** switch (location) { case CustomerLocation.EastCoast: return new EastCoastDistributor(); case CustomerLocation.WestCoast: return new WestCoastDistributor(); } return null; } IAdvertiser IBookStore.GetAdvertiser() { //internal logic on which distributor to return //*** logic can be changed without changing the client code **** switch (location) { case CustomerLocation.EastCoast: return new BlueAdvertiser(); case CustomerLocation.WestCoast: return new RedAdvertiser(); } return null; } } //the product public interface IDistributor { void ShipBook(); } //concrete product public class EastCoastDistributor : IDistributor { void IDistributor.ShipBook() { Console.WriteLine("Book shipped by East Coast Distributor"); } } //concrete product public class WestCoastDistributor : IDistributor { void IDistributor.ShipBook() { Console.WriteLine("Book shipped by West Coast Distributor"); } } //the product public interface IAdvertiser { void Advertise(); } //concrete product public class RedAdvertiser : IAdvertiser { void IAdvertiser.Advertise() { Console.WriteLine("Advertised by RedAdvertiser"); } } //concrete product public class BlueAdvertiser : IAdvertiser { void IAdvertiser.Advertise() { Console.WriteLine("Advertised by BlueAdvertiser"); } }
SOURCE:
No comments:
Post a Comment