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

发布于 2025-02-06 01:58:26 字数 1592 浏览 1 评论 0原文

我有下面的两个类别:

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

I have two classes like below:

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

The constraint is: I can not create just one class instead of two separate classes A and B as they have significant differences in usage.

Now, my question is: how can I clean this code up having both classes A and B but a single method for GernerateStuff?

I can create an interface like this:

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

Then, the problem is how public async IInterface GernerateStuff(int expireDays = 15) signature would handle both class A and B?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

余罪 2025-02-13 01:58:26

TOT实现这一目标有几种方法。在其他答案中已经提到了一个,这是关于使用仿制药的。但是,这假设您的类型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 { ... }
}

此解决方案具有优势,当您向工厂添加新类型时,您无需更改客户端逻辑。您需要更改的只是通过引入新的C {...}来更改工厂本身。此外,客户无法提供任何实际不起作用的类型,因为它们根本不提供任何类型的信息。

顺便说一句,您的方法不会等待任何东西,因此没有理由将其制作async

There are a couple of ways tot achieve this. One has already been mentioned in other answeres, which is about using generics. However this assumes your types A and B do even have anything in common that you could use as common generic constraint - e.g a common base-interface. Then you could do this:

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

You could also create a factory that creates the instances of A or B depending on some condition - e.g. some configuration. However you'd also need a common base-interface for that:

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 { ... }
}

This solution has the advantage, that you don't need to change the client-logic when you add a new type to the factory. All you need to change is the factory itself by introducing new C { ... }. Furthermor a client cannot provide any types that actually don't work, as they don't provide any type-information at all.

By the way your method does not await anything, so there's no reason to make it async.

自我难过 2025-02-13 01:58:26

创建静态出厂方法并将其注入参数:

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在没有等待的情况下不会有任何意义,因此请摆脱它。

Create a static factory method and inject it as a parameter:

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

But if class A and B are identical, why not use the same class? This is a definite code smell, and you should probably think about how you model your problem some more. Also, async does not serve any point without an await, so get rid of it.

农村范ル 2025-02-13 01:58:26

我在聚会上有点迟了,但是您可以将词典与代表使用可以提供懒惰初始化的代表。

让我举个例子。首先,我们需要进行枚举以避免在创建类实例时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)();

I am little bit late at the party, however you can use dictionary with delegates which can provide lazy initialization.

Let me show an example. At first, we need to make enums to avoid if else statements while creating instances of classes:

public enum FooType
{
    A, B
}

Then we need to have abstract base class which has the same behaviour for all derived classes:

public abstract class FooBase
{
    public string? Stuff { get; set; }
    public DateTime Created { get; set; }
    public DateTime Expires { get; set; }
}

And derived classes look like this:

public class A : FooBase
{
    public A(int expireDays)
    {
    }

    public override string ToString() => 
quot;I am A class";
    
}

public class B : FooBase
{
    public B(int expireDays)
    {
    }

    public override string ToString() => 
quot;I am B class";
}

Then Factory looks like this and you can customize any property or method if you want to:

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

And you can use it like this:

FooFactory factory = new FooFactory();
A a = (A)factory.GetInstance(FooType.A, 11)();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文