Страницы

Поиск по вопросам

вторник, 26 ноября 2019 г.

Как сделать 3 конструктора с одинаковой сигнатурой?


Нужно написать программу, содержащую класс Triangle. В данном классе должно быт
три конструктора:  


Конструктор, принимающий три стороны.  
Конструктор, принимающий две стороны и угол между ними и рассчитывающий третью сторону по теореме косинусов.  
Конструктор, принимающий два угла и сторону между ними и рассчитывающий оставшиеся стороны по теореме синусов (памятуя о теореме о сумме углов треугольника).


Каждый Triangle также должен возвращать собственную площадь.

То есть имеем следующее:  

class Triangle
{
    Triangle(double side1, double side2, double side3)
    {
    }
    Triangle(double side1, double side2, double angle)
    {
    }
    Triangle(double side1, double angle1, double angle2)
    {
    }
}


Вся проблема в том, что у всех трех конструкторов одинаковая сигнатура, но разная логика.
Вопрос: как решить эту проблему "правильно"?  

"Неправильные" варианты: 


Замена параметра(ов) double на string и дальнейший парсинг string в double.  
Замена параметра(ов) double на float.
Вынесение параметров в массив:  

Triangle(double[] sides, double angle)

Создание нового типа данных Angle.

    


Ответы

Ответ 1



Вариант 1 - используйте именованные конструкторы. Именованным конструктором называется статический метод, задача которого - создат объект с заданными параметрами. Иногда их еще не вполне корректно называют фабричными методами. class Triangle { private Triangle() {} public static Triangle From3Sides(double side1, double side2, double side3) { ... } public static Triangle From2SidesAndAngle(double side1, double side2, doubl angle) { ... } public static Triangle FromSideAnd2Angles(double side, double angle1, doubl angle2) { ... } } Вариант 2 - использовать доменные типы данных Определяем типы данных "длина" и "угол" - и используем их в конструкторах. class Length { public double Value { get; } public Length() {} public Length(double value) { Value = value; } public static implicit operator double (Length length) => length.Value; public static implicit operator Length (double value) => new Length(value); } class Angle { public double Value { get; } public Angle() {} public Angle(double value) { Value = value; } public static implicit operator double (Angle angle) => angle.Value; public static implicit operator Angle (double value) => new Angle(value); }

Ответ 2



Как вариант сгруппировать наборы параметров в структуры: public struct ThreeSides { double side1; double side2; double side3; } public struct TwoSidesAndAngle { double side1; double side2; double angle; } public struct SideAndTwoAngles { double side1; double angle1; double angle2; } public class Triangle { public Triangle(ThreeSides params) { } public Triangle(TwoSidesAndAngle params) { } public Triangle(SideAndTwoAngles params) { } } но мне кажется более правильный вариант с фабриками в ответе Павла

Ответ 3



Пара альтернативных вариантов к уже предложенным. Т.к. выбор перегруженной функции (и конструктора в том числе) осуществляется на основани типов, понятно, что создать несколько разных конструкторов с одинаковыми сигнатурами не получится. Проблему можно решить путем добавления дополнительного параметра. Заводятся новые "пустые" классы (имена взял из ответа @Argon): class ThreeSides {} class TwoSidesAndAngle {} class SideAndTwoAngles {} Сигнатуры конструкторов в этом случае будут следующие: Triangle(double side1, double side2, double side3, ThreeSides unused); Triangle(double side1, double side2, double angle, TwoSidesAndAngle unused); Triangle(double side1, double angle1, double angle2, SideAndTwoAngles unused); Используется перечисление enum. enum Kind { ThreeSides, TwoSidesAndAngle, SideAndTwoAngles } В этом случае конструктор будет единственный с ветвлением логики внутри: Triangle(double arg1, double arg2, double arg3, Kind kind); При этом для аргумента kind можно задать значение по умолчанию, если планируется, что будет преобладать тот или иной вариант построения треугольника. Например: Triangle(double arg1, double arg2, double arg3, Kind kind = Kind.ThreeSides);

Ответ 4



При желании можно вытворить что-то такое: http://ideone.com/XLf5Pz using System; class Triangle { public struct NeverCreateMe {} public Triangle(double side1, double side2, double side3) { Console.WriteLine("3 sides"); } public Triangle(double side1, double side2, NeverCreateMe ignored = default(NeverCreateMe), double angle = 0) { Console.WriteLine("2 sides and 1 angle"); } public Triangle(double side1, NeverCreateMe ignored = default(NeverCreateMe), double angle1 = 0, double angle2 = 0) { Console.WriteLine("1 side and 2 angles"); } } public class Test { public static void Main() { var a = new Triangle(1.0, 2.0, 3.0); var b = new Triangle(1.0, 2.0, angle: 3.0); var c = new Triangle(1.0, angle1: 2.0, angle2: 3.0); } } Или даже такое: http://ideone.com/VEGn2s using System; class Triangle { public struct NeverCreateMe {} public Triangle(double side1, double side2, double side3) { Console.WriteLine("3 sides"); } public Triangle(double side1, double side2, double angle, NeverCreateMe ignored = default(NeverCreateMe)) { Console.WriteLine("2 sides and 1 angle"); } public Triangle(double side1, double angle1, double angle2, NeverCreateMe ignored = default(NeverCreateMe)) { Console.WriteLine("1 side and 2 angles"); } } public class Test { public static void Main() { var a = new Triangle(1.0, 2.0, 3.0); var b = new Triangle(1.0, 2.0, angle: 3.0); var c = new Triangle(1.0, angle1: 2.0, angle2: 3.0); } } У каждого способа есть свои плюсы и минусы, но при желании их можно решить.

Комментариев нет:

Отправить комментарий