Нужно написать программу, содержащую класс 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);
}
}
У каждого способа есть свои плюсы и минусы, но при желании их можно решить.
Комментариев нет:
Отправить комментарий