英文:
Extending interface signature without editing code in multiple places
问题
I have an interface and existing code which implements this interface:
public interface IBusinessService<T> where T : class
{
Task Add(T category);
Task Delete(T category);
Task Update(T category);
Task<IEnumerable<T>> GetAll();
Task<T> GetById(int id);
}
Now some id
has the type of Guid
. So I cannot use this method to send a Guid
id:
var id = Guid.NewGuid()
var result = GetById(id);
So we need that id
parameter can be of the type Guid
. It would be ideal if it is possible:
public interface IBusinessService<T> where T : class
{
/* ... the other code is omitted for brevity */
Task<T> GetById(Guid or int id );
}
What I thought to implement is to create a new method with a parameter type of Guid
:
public interface IBusinessService<T> where T : class
{
Task Add(T category);
Task Delete(T category);
Task Update(T category);
Task<IEnumerable<T>> GetAll();
Task<T> GetById(int id);
Task<T> GetById(Guid id);
}
But if I do this, then I need to edit so much code. So it looks like it is not a good solution.
Is there a way to add another type of id
to the interface method GetById
without breaking changes?
英文:
I have an interface and existing code which implements this interface:
public interface IBusinessService<T> where T : class
{
Task Add(T category);
Task Delete(T category);
Task Update(T category);
Task<IEnumerable<T>> GetAll();
Task<T> GetById(int id);
}
Now some id
has type of Guid
. So I cannot use this method to send Guid
id:
var id = Guid.NewGuid()
var result = GetById(id);
So we need that id
parameter can be type of Guid
. It would be ideal if it is possible:
public interface IBusinessService<T> where T : class
{
/* ... the other code is omitted for the brevity */
Task<T> GetById(Guid or int id );
}
What I thought to implement is to create a new method with parameter type of Guid
:
public interface IBusinessService<T> where T : class
{
Task Add(T category);
Task Delete(T category);
Task Update(T category);
Task<IEnumerable<T>> GetAll();
Task<T> GetById(int id);
Task<T> GetById(Guid id);
}
But if I will do this, then I need to edit so many code. So it looks like it is not a good solution.
Is there a way to add another type of id
to the interface method GetById
without breaking changes?
答案1
得分: 1
这似乎是一个设计问题(接口/API设计)首先。从一开始就应该清楚ID是由Guid
还是原始的int
值表示。不应该两者兼容。通常,您尝试使用协议来控制客户端-服务器通信,或者至少通过定义服务器API来实现。这个协议将定义API期望的值,客户端必须确保以正确的格式输入它们。
考虑继续使用int
ID,并在需要时使用自定义转换从Guid
到int
。或者引入一个表(Dictionary
),在其中存储Guid
和int
值的对。因此,对于进入您的应用程序的每个Guid
,您内部生成一个整数,它将替换Guid
。但在使用int
值时,必须有一些约束或算法来确保ID的唯一性。但我假设当您使用int
作为唯一ID时,已经有一个生成这些ID的算法了。
向接口添加成员始终是一个破坏性的更改。
由于您没有使用C# 8,无法使用新的默认接口方法功能。
因此,如果可以接受破坏性更改,那么可以重构接口以接受一个额外的泛型参数用于id
参数。您还可以将id
参数的类型从int
更改为object
。但这也会引入装箱/拆箱的成本(以将值类型如int
转换为object
)。我个人认为使用object
或string
中的任何一个都是最好的选择。我不了解您的Web接口,但string
可能是首选的ID类型。
因此,在无法接受这些破坏性更改的情况下,最好的解决方案是实现一个新的接口,例如IGuidIdBusinessService<T>
:
public interface IGuidIdBusinessService<T> : IBusinessService<T> where T : class
{
Task<T> GetByGuid(Guid id);
}
实现这些接口:
public class MyClass : IGuidIdBusinessService<object>
{
#region Implementation of IBusinessService<object>
...
// Either throw NotSupportedException or implement conversion
public async Task<object> GetById(int id)
{
Guid guid = ConvertIntToGuid(id);
return GetByGuid(guid);
}
#endregion
#region Implementation of IGuidIdBusinessService<object>
public async Task<object> GetByGuid(Guid id) => throw new NotImplementedException();
#endregion
}
希望这些信息对您有所帮助。
英文:
This seems to be a design problem (interface/API design) in the first place. It should be clear from the beginning whether IDs are represented by a Guid
or a raw int
value. But not both. Usually you try to use a protocol to control the client-server communication or at least by defining a server API. This protocol would define what values the API expects and the client must take care to input them in the correct format.
Consider to stay with the int
ID an use a custom conversion from Guid
to int
where required. Alternatively introduce a table (Dictionary
) where you store pairs of Guid
and int
values. So for each Guid
that enters your application you internally generate a integer which will replace the Guid
. But there must be some constraints or algorithm to guarantee the uniqueness of the IDs especially when using int
values. But I assume when you are using int
for unique IDs you already have an algorithm to generate those IDs.
Adding members to an interface is always a breaking change.
Since you are not using C# 8 you can't make use of the new Default Interface Method feature.
So, if you can accept breaking changes then you could refactor the interface to take one more generic parameter for the id
parameter.
You could also change the type of the id
parameter from int
to object
. But this would also introduce the costs of boxing/unboxing (in order to convert value types like int
to object
). I personally think using either object
or string
would be the best. I don't know your web interface but string
could be the preferred ID type.
So the best solution, in case you can't accept those breaking changes, would be to implement a new interface e.g., IGuidIdBusinessService<T>
:
public interface IGuidIdBusinessService<T> : IBusinessService<T> where T : class
{
Task<T> GetByGuid(Guid id);
}
Implementing the interfaces:
public class MyClass : IGuidIdBusinessService<object>
{
#region Implementation of IBusinessService<object>
...
// Either throw NotSupportedException or implement conversion
public async Task<object> GetById(int id)
{
Guid guid = ConvertIntToGuid(id);
return GetByGuid(guid);
}
#endregion
#region Implementation of IGuidIdBusinessService<object>
public async Task<object> GetByGuid(Guid id) => throw new NotImplementedException();
#endregion
}
答案2
得分: 1
I really don't see the problem as big as you describe -- "But if I will do this, then I need to edit so many code"
How many implementations do you have. 20? Key here, implementation vs usages. Usages can be many or very many.
Looks like your goal is to keep usage intact as
var id = Guid.NewGuid();
var result = someInst.GetById(id);
// or
var id = 5;
var result = someInst.GetById(id);
No problem, you need minimal changes. This happens all the time
public interface IBusinessService<T> where T : class
{
Task<T> GetById(object id); // NOTE - change to 'object'
}
Now, you just need to change only a few of your implementations that you have to distinguish Guid
from int
public Task<T> GetById(object id)
{
if (id is int i)
return ProcessIdAsInt<T>(i);
else if (id is Guid g)
return ProcessIdAsGuid<T>(g);
else
throw new InvalidOperationException("Supplied data type not supported");
}
So, the changes are minimal. Usages will remain intact. Nothing unusual.
英文:
I really don't see the problem as big as you describe -- "But if I will do this, then I need to edit so many code"
How many implementations do you have. 20? Key here, implementation vs usages. Usages can be many or very many.
Looks like your goal is to keep usage intact as
var id = Guid.NewGuid();
var result = someInst.GetById(id);
// or
var id = 5;
var result = someInst.GetById(id);
No problem, you need minimal changes. This happens all the time
public interface IBusinessService<T> where T : class
{
Task<T> GetById(object id); // NOTE - change to 'object'
}
Now, you just need to change only few of your implementations that you have to distinct Guid
from int
public Task<T> GetById(object id)
{
if (id is int i)
return ProcessIdAsInt<T>(i);
else if (id is Guid g)
return ProcessIdAsGuid<T>(g);
else
throw new InvalidOperationException("Supplied data type not supported");
}
So, the changes are minimal. Usages will remain intact. Nothing unusual.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论