Factory Method and Abstract Factory patterns both avoid direct instantiation of new instances by the caller. Instead, they encapsulate the creation logic within factory code, configuring instances beforehand and allocating them for the caller's use.
Original Code:
HttpRequest
is responsible for checking if a URL is accessible and its health status.
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Children
{
class Program
{
static void Main(string[] args)
{
HttpRequest request = new HttpRequest("https://whuanle.cn");
HttpStatusCode code = request.GetAsync().Result;
Console.WriteLine("Website Status: " + Enum.GetName(code.GetType(), code));
Console.ReadKey();
}
}
/// <summary>
/// Code Factory
/// </summary>
public class HttpRequest
{
private HttpClient httpClient;
private Uri uri;
public HttpRequest(string url)
{
uri = new Uri(url);
httpClient = new HttpClient();
}
public async Task<HttpStatusCode> GetAsync()
{
var request = await httpClient.GetAsync(uri);
return request.StatusCode;
}
}
}
New issues arise here:
- Users have to pass the URL themselves, which can lead to errors.
- It limits access to only a few predefined websites and does not allow arbitrary URL inputs.
- ...
First, we can enhance it using the Factory Method pattern:
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Children
{
class Program
{
static void Main(string[] args)
{
IRequest request = Factory.Create(UrlType.Baidu);
HttpStatusCode code = request.GetAsync().Result;
Console.WriteLine("Website Status: " + Enum.GetName(code.GetType(), code));
Console.ReadKey();
}
}
#region
public interface IRequest
{
Task<HttpStatusCode> GetAsync();
}
public class BaiduRequest : IRequest
{
private HttpClient httpClient = new HttpClient();
private Uri uri = new Uri("https://www.baidu.com");
public async Task<HttpStatusCode> GetAsync()
{
var request = await httpClient.GetAsync(uri);
return request.StatusCode;
}
}
public class QQRequest : IRequest
{
private HttpClient httpClient = new HttpClient();
private Uri uri = new Uri("https://www.qq.com");
public async Task<HttpStatusCode> GetAsync()
{
var request = await httpClient.GetAsync(uri);
return request.StatusCode;
}
}
#endregion
#region
public enum UrlType
{
Baidu = 1,
QQ = 2
}
public static class Factory
{
public static IRequest Create(UrlType type)
{
switch (type)
{
case UrlType.Baidu:
return new BaiduRequest();
case UrlType.QQ:
return new QQRequest();
}
return default;
}
}
#endregion
}
However, a new problem emerges: a website can have multiple pages, necessitating checks for various page statuses.
Assuming both websites have two pages, one named One and the other Two, we can implement the Abstract Factory pattern as follows:
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Children
{
class Program
{
static void Main(string[] args)
{
ICreateFactory factory = FactoryFactory.Create(UrlType.BAIDU);
IRequest request = factory.One();
HttpStatusCode code = request.GetAsync().Result;
Console.WriteLine("Website Status: " + Enum.GetName(code.GetType(), code));
Console.ReadKey();
}
}
#region Common Inheritance
// Interface for the Factory Method to be used by the Abstract Factory pattern
public interface ICreateFactory
{
IRequest One();
IRequest Two();
}
// Accessing Pages
public interface IRequest
{
Task<HttpStatusCode> GetAsync();
}
#endregion
#region Accessing Baidu Encyclopedia Pages
public abstract class IBaiduRequest : IRequest
{
protected HttpClient httpClient = new HttpClient();
protected Uri baseUri = new Uri("https://baike.baidu.com/");
public abstract Task<HttpStatusCode> GetAsync();
}
public class Baidu_DOTNET_Request : IBaiduRequest
{
protected Uri thisUri;
public Baidu_DOTNET_Request()
{
Uri.TryCreate(baseUri, "item/.NET", out thisUri);
}
public override async Task<HttpStatusCode> GetAsync()
{
var request = await base.httpClient.GetAsync(thisUri);
return request.StatusCode;
}
}
public class Baidu_Microsoft_Request : IBaiduRequest
{
protected Uri thisUri;
public Baidu_Microsoft_Request()
{
Uri.TryCreate(baseUri, "item/Microsoft", out thisUri);
}
public override async Task<HttpStatusCode> GetAsync()
{
var request = await base.httpClient.GetAsync(thisUri);
return request.StatusCode;
}
}
#endregion
#region Accessing QQ
public abstract class IQQRequest : IRequest
{
protected HttpClient httpClient = new HttpClient();
protected Uri baseUri = new Uri("https://new.qq.com/");
public abstract Task<HttpStatusCode> GetAsync();
}
public class QQ_Images_Request : IQQRequest
{
protected Uri thisUri;
public QQ_Images_Request()
{
Uri.TryCreate(baseUri, "ch/photo", out thisUri);
}
public override async Task<HttpStatusCode> GetAsync()
{
var request = await base.httpClient.GetAsync(thisUri);
return request.StatusCode;
}
}
public class QQ_OMN_Request : IQQRequest
{
protected Uri thisUri;
public QQ_OMN_Request()
{
Uri.TryCreate(baseUri, "omn/author/5114481", out thisUri);
}
public override async Task<HttpStatusCode> GetAsync()
{
var request = await base.httpClient.GetAsync(thisUri);
return request.StatusCode;
}
}
#endregion
#region Factory Method and Abstract Factory Patterns
// Factory Method for accessing Baidu
public class CreateBaidu : ICreateFactory
{
public IRequest One()
{
return new Baidu_DOTNET_Request();
}
public IRequest Two()
{
return new Baidu_Microsoft_Request();
}
}
// Factory Method for accessing QQ
public class CreateQQ : ICreateFactory
{
public IRequest One()
{
return new QQ_Images_Request();
}
public IRequest Two()
{
return new QQ_OMN_Request();
}
}
public enum UrlType
{
BAIDU = 1,
QQ = 2
}
// Abstract Factory Pattern
public static class FactoryFactory
{
public static ICreateFactory Create(UrlType type)
{
switch (type)
{
case UrlType.BAIDU:
return new CreateBaidu();
case UrlType.QQ:
return new CreateQQ();
}
return default;
}
}
#endregion
}
There are many variations of Factory Method and Abstract Factory patterns.
The Factory Method pattern is a single layer where a factory class creates types;
The Abstract Factory pattern creates a factory (one layer) using the Factory Method, which continues to create new factories (two layers) and subsequently creates types.
文章评论