我有兩個類,如下所示:
public async A GernerateStuff(int expireDays = 15)
{
using var randomNumberGenerator = RandomNumberGenerator.Create();
var randomBytes = new byte[64];
var now = DateTime.UtcNow;
randomNumberGenerator.GetBytes(randomBytes);
return new A
{
Stuff = Convert.ToBase64String(randomBytes),
Created = now,
Expires = now.AddDays(expireDays)
};
}
public async B GernerateStuff(int expireDays = 10)
{
using var randomNumberGenerator = RandomNumberGenerator.Create();
var randomBytes = new byte[64];
var now = DateTime.UtcNow;
randomNumberGenerator.GetBytes(randomBytes);
return new B
{
Stuff = Convert.ToBase64String(randomBytes),
Created = now,
Expires = now.AddDays(expireDays)
};
}
public class A
{
public string Stuff{ get; set; }
public DateTime Created { get; set; }
public DateTime Expires { get; set; }
}
public class B
{
public string Stuff{ get; set; }
public DateTime Created { get; set; }
public DateTime Expires { get; set; }
}
約束是:我不能只創建一個類而不是兩個單獨的類 A 和 B,因為它們在使用上存在顯著差異。
現在,我的問題是:我怎樣才能清理這段代碼,同時擁有 A 和 B 類,但只有一個方法GernerateStuff?
我可以創建這樣的界面:
public class A : IInterface
{
}
public class B : IInterface
{
}
public interface IInterface
{
public string Stuff{ get; set; }
public DateTime Created { get; set; }
public DateTime Expires { get; set; }
}
那么,問題是public async IInterface GernerateStuff(int expireDays = 15)簽名如何處理 A 類和 B 類?
uj5u.com熱心網友回復:
有幾種方法可以實作這一目標。其他答案中已經提到了一個,即使用泛型。但是,這假設您的型別A,B甚至確實有任何共同點,您可以將其用作通用通用約束 - 例如通用基礎介面。然后你可以這樣做:
public async T GernerateStuff<T>(int expireDays = 15) where T: new(), MyInterface
{
using var randomNumberGenerator = RandomNumberGenerator.Create();
var randomBytes = new byte[64];
var now = DateTime.UtcNow;
randomNumberGenerator.GetBytes(randomBytes);
return new T
{
Stuff = Convert.ToBase64String(randomBytes),
Created = now,
Expires = now.AddDays(expireDays)
};
}
您還可以創建一個工廠來創建A或B根據某些條件創建實體 - 例如某些配置。但是,您還需要一個通用的基本介面:
public async MyInterface GernerateStuff(int expireDays = 15)
{
using var randomNumberGenerator = RandomNumberGenerator.Create();
var randomBytes = new byte[64];
var now = DateTime.UtcNow;
randomNumberGenerator.GetBytes(randomBytes);
return CreateTheThing(Convert.ToBase64String(randomBytes), now, now.AddDays(expireDays));
}
MyInterface CreateTheThing(string stuff, DateTime created, DateTime expires)
{
if(...)
return new A { ... }
else if(...)
return new B { ... }
return new C { ... }
}
此解決方案的優點是,當您向工廠添加新型別時,您不需要更改客戶端邏輯。您需要通過引入來更改工廠本身new C { ... }。此外,客戶端無法提供任何實際上不起作用的型別,因為它們根本不提供任何型別資訊。
順便說一句,您的方法沒有await任何作用,因此沒有理由這樣做async。
uj5u.com熱心網友回復:
創建一個靜態工廠方法并將其作為引數注入:
public static A CreateA(byte[] stuff, DateTime now, DateTime expires)
=> new A{ Stuff = stuff, Created = now, Expires = expires }
...
public static T GernerateStuff<T>(Func<byte[], DateTime, DateTime, T> ctor, int expireDays = 15)
{
using var randomNumberGenerator = RandomNumberGenerator.Create();
var randomBytes = new byte[64];
var now = DateTime.UtcNow;
randomNumberGenerator.GetBytes(randomBytes);
return ctor(
Convert.ToBase64String(randomBytes),
now,
now.AddDays(expireDays)
;
}
...
var a = GernerateStuff(CreateA);
但是如果A類和B類是相同的,為什么不使用同一個類呢?這是一種明確的代碼氣味,您可能應該考慮如何對問題進行更多建模。此外,async沒有等待就沒有任何意義,所以擺脫它。
uj5u.com熱心網友回復:
我在聚會上有點晚了,但是您可以將字典與可以提供延遲初始化的委托一起使用。
讓我舉個例子。首先,我們需要if else在創建類實體時創建列舉以避免陳述句:
public enum FooType
{
A, B
}
然后我們需要有對所有派生類具有相同行為的抽象基類:
public abstract class FooBase
{
public string? Stuff { get; set; }
public DateTime Created { get; set; }
public DateTime Expires { get; set; }
}
派生類如下所示:
public class A : FooBase
{
public A(int expireDays)
{
}
public override string ToString() => $"I am A class";
}
public class B : FooBase
{
public B(int expireDays)
{
}
public override string ToString() => $"I am B class";
}
然后工廠看起來像這樣:
public class FooFactory
{
Dictionary<FooType, Func<FooBase>> _fooBaseByType;
public Func<FooBase> GetInstance(FooType fooType, int expireDays)
{
_fooBaseByType = new Dictionary<FooType, Func<FooBase>>
{
{ FooType.A, () => new A(expireDays) { Created = DateTime.Now} },
{ FooType.B, () => new B(expireDays) { Created = DateTime.Now} }
};
return _fooBaseByType[fooType];
}
}
你可以像這樣使用它:
FooFactory factory = new FooFactory();
A a = (A)factory.GetInstance(FooType.A, 11)();
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/489514.html
