Прочитав эти ответы, они довольно хорошо объясняют, что делает новый оператор, но я не вижу четкого ответа на эту часть вопроса ОП:
Знаете ли вы сценарий, в котором было бы полезно спрятаться в интерфейсе?
Таким образом, все сводится к тестируемости и повторному использованию. Разбивая интерфейсы на более мелкие фрагменты и придерживаясь принципа разделения интерфейсов, мы можем сделать пользователей наших классов минимально зависимыми от несущественных деталей и максимально развязанными, что дает нам больше вариантов повторного использования и упрощает тестирование.
В общем, новый оператор вступает в игру здесь, когда нам нужно разветвить иерархию типов нашего интерфейса таким образом, чтобы неизбежны коллизии методов. Все это звучит немного абстрактно и трудно объяснить, поэтому я создал то, что я считаю минимальным примером, где мы хотим разделить нашу иерархию типов интерфейса на две, в то время как есть общий общий метод. Я помещаю код на скрипку .NET:
https://dotnetfiddle.net/kRQpoU
Вот еще раз:
using System;
public class Program
{
public static void Main()
{
//Simple usage
var simpleCuboid = new MockCuboidSimple();
var heightUser = new HeightUserEntangled();
var volumeUser = new VolumeUserEntangled();
Console.WriteLine("*** Simple Case ***");
Console.WriteLine(heightUser.PrintHeight(simpleCuboid));
Console.WriteLine(volumeUser.PrintVolume(simpleCuboid));
//Smart usage - the same behaviour, but more testable behind the scenes!
var smartCuboid = new MockCuboidSmart();
var heightUserSmart = new HeightUserSmart();
var volumeUserSmart = new VolumeUserSmart();
Console.WriteLine("*** smart Case ***");
Console.WriteLine(heightUserSmart.PrintHeight(smartCuboid));
Console.WriteLine(volumeUserSmart.PrintVolume(smartCuboid));
}
}
//Disentangled
class VolumeUserSmart
{
public string PrintVolume(IThingWithVolume volumeThing)
{
return string.Format("Object {0} has volume {1}", volumeThing.Name, volumeThing.Volume);
}
}
class HeightUserSmart
{
public string PrintHeight(IThingWithHeight heightThing)
{
return string.Format("Object {0} has height {1}", heightThing.Name, heightThing.Height);
}
}
class MockCuboidSmart : ICuboidSmart
{
public string Name => "Mrs. Cuboid";
public double Height => 3.333;
public double Width => 31.23432;
public double Length => 123.12;
public double Volume => Height * Width * Length;
}
interface ICuboidSmart : IThingWithHeight, IThingWithVolume
{
//Here's where we use new, to be explicit about our intentions
new string Name {get;}
double Width {get;}
double Length {get;}
//Potentially more methods here using external types over which we have no control - hard to mock up for testing
}
interface IThingWithHeight
{
string Name {get;}
double Height {get;}
}
interface IThingWithVolume
{
string Name {get;}
double Volume {get;}
}
//Entangled
class VolumeUserEntangled
{
public string PrintVolume(ICuboidSimple cuboid)
{
return string.Format("Object {0} has volume {1}", cuboid.Name, cuboid.Volume);
}
}
class HeightUserEntangled
{
public string PrintHeight(ICuboidSimple cuboid)
{
return string.Format("Object {0} has height {1}", cuboid.Name, cuboid.Height);
}
}
class MockCuboidSimple : ICuboidSimple
{
public string Name => "Mrs. Cuboid";
public double Height => 3.333;
public double Width => 31.23432;
public double Length => 123.12;
public double Volume => Height * Width * Length;
}
interface ICuboidSimple
{
string Name {get;}
double Height {get;}
double Width {get;}
double Length {get;}
double Volume {get;}
//Potentially more methods here using external types over which we have no control - hard to mock up for testing
}
Обратите внимание, что VolumeUserSmart
и HeightUserSmart
зависят только от тех фрагментов интерфейса ICuboidSmart
, которые им нужны, а именно IThingWithHeight
и IThingWithVolume
. Таким образом, они могут быть максимально повторно использованы, т.е. для фигур, отличных от кубоидов, а также их легче проверить. Последний пункт, как я нахожу на практике, имеет решающее значение. Гораздо проще смоделировать интерфейс с меньшим количеством методов, особенно если методы основного типа интерфейса содержат ссылки на типы, которые мы не контролируем. Конечно, всегда можно обойти это с помощью мок-фреймворка, но я предпочитаю, чтобы код оставался чистым в своей основе.
Итак, при чем здесь ключевое слово new
? Что ж, поскольку VolumeUserSmart
и HeightUserSmart
оба нуждаются в доступе к свойству Name
, мы должны объявить его как в IThingWithHeight
, так и в IThingWithVolume
. И поэтому мы должны повторно объявить его в подинтерфейсе ICuboidSmart
, иначе мы получим ошибку компилятора, жалующуюся на двусмысленность. В этом случае мы скрываем две версии Name
, определенные в IThingWithHeight
и IThingWithVolume
, которые в противном случае столкнулись бы. И, как указывают другие ответы, хотя мы не должны использовать здесь new
, мы должны явно указать наши намерения скрыться.
person
Colm Bhandal
schedule
04.06.2020