如何用两个相同的类名和具有不同返回类型的相同助手方法重构此代码?



我有如下两个类:

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类?

有几种方法可以实现这一点。其中一个已经在其他答案中提到了,那就是使用泛型。然而,这假设你的类型AB甚至有任何共同之处,你可以用作共同的通用约束-例如,一个共同的基本接口。然后你可以这样做:

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)
};
}

您还可以创建一个工厂,根据某些条件(例如某些配置)创建AB的实例。但是,您还需要一个通用的基本接口:

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

创建一个静态工厂方法并将其作为参数注入:

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在没有await的情况下不会为任何点提供服务,因此请删除它。

我有点晚了,但是你可以使用字典和委托,它可以提供延迟初始化。

让我举个例子。首先,在创建类的实例时,我们需要创建枚举来避免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 = SomeCustomDateTime(), 
Stuff = SomeCustomString()
} 
},
{   
FooType.B, () => new B(expireDays) 
{ 
Created = SomeCustomDateTime()
} 
}
};
return _fooBaseByType[fooType];
}
private DateTime SomeCustomDateTime() => DateTime.Now;
private string SomeCustomString() => "My custom string";
}

你可以这样使用:

FooFactory factory = new FooFactory();
A a = (A)factory.GetInstance(FooType.A, 11)();

相关内容

  • 没有找到相关文章

最新更新