Задача заключалась в создании класса треугольник с заданными сторонами. Нужно было сделать проверку, что это действительно треугольник. Ну и плюс подсчитать его параметры.
Вот пример кода (на C++), с которым мы и будем разбираться:
class triangle
{
private:
double x,y,z;
public:
triangle()
{
x=1;y=1;z=1;
}
triangle(double a,double b,double c)
{
if (a+b>c && c+b>a && a+c>b)
{
a=x; b=y; c=z;
}
else
cout<<"please try one's more"<<endl;
}
double sumSide(double a,double b,double c)
{
return (a+b+c);
}
double square(double a,double b,double c)
{
double p=sumSide(a,b,c)/2;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
void type(double a,double b,double c)
{
if ( ( a*a > b*b+c*c ) || ( b*b > a*a+c*c ) || ( c*c > a*a+c*c ) )
cout<<"one angle is more that 90 digreas"<<endl;
else
........удалил часть кода.........
}
void angle (double a,double b,double c)
{
double alpha,betta,gamma;
alpha=acos( (b*b+c*c-a*a)/(2*(b*c)) );
betta=acos( (b*b+a*a-c*c)/(2*(b*a)) );
gamma=acos( (c*c+a*a-b*b)/(2*(c*a)) );
cout<<"alpha= "<<alpha<<endl;
cout<<"betta= "<<betta<<endl;
cout<<"gamma= "<<gamma<<endl;
}
};
int main()
{
freopen("input.txt","rt",stdin);
freopen("output.txt","wt",stdout);
double a,b,c;
cin>>a;
cin>>b;
cin>>c;
triangle firstTriangle(a,b,c);
cout<<firstTriangle.square(a,b,c)<<endl;
firstTriangle.type(a,b,c);
firstTriangle.angle(a,b,c);
return 0;
}
Что мне не нравится и вызывает вопросы:
· Почему в конструкторе создается треугольник 1/1/1? Нужен ли вообще такой конструктор? Ведь есть же тот, который сразу принимает три параметра. Мне кажется что пустой конструктор просто не нужен. Но тут зависит от задачи.
· Во втором конструкторе проверка что это не треугольник есть, но – если это не треугольник, то выводится сообщение и внутренние переменные не инициализируются вообще. Т.е. в результате получается не понятный объект с непонятными полями.
· Выводить сообщения на консоль в конструкторе и вообще в других методах – не правильно. Это не задача класса треугольник сообщать пользователю об ошибках. Это задача того класса, который пытается создать этот треугольник. А уже как он будет его выводить – другой вопрос. Может быть это вообще будет WinForms-приложение, а не консольное.
· Мне одному кажется что этот код вообще не работает и должно быть ровно наоборот: x=a; y=b; z=c; а не как сейчас?
· Зачем вообще нужны эти x,y,z если они не используются нигде, а везде суется a,b,c? По идее методы sumSize и все другие вообще не должны иметь параметров, а использовать x,y,z. См. предыдущий пункт - оно и работать тогда не будет.
· Метод type по идее должен возвращать тип треугольника, а не выводить его на консоль.
Дополнительно:
· Не проверяется что файл открыт успешно и из него можно читать
· Не проверяется что прочитано хоть что-то
· Не проверяется что файл создан и в него можно писать
Я попробую описать несколько возможных вариантов реализации.
Вариант 1 – генерация исключений в конструкторе
Этот вариант хорошо работает для C# и не очень хорошо для С++.
public class Triangle
{
private double x;
private double y;
private double z;
public Triangle(double x, double y, double z)
{
if (Check(x, y, z))
{
this.x = x;
this.y = y;
this.z = z;
}
else
{
throw new ApplicationException("Неверные параметры треугольника");
}
}
private bool Check(double x, double y, double z)
{
// проверяем все что надо и возвращаем true/false
// если надо – можем тут кинуть дополнительные исключения
}
}
В этом случае создать некорректный объект просто не получится, о чем мы и сообщим тому, кто будет создавать:
try
{
Triangle t = new Triangle(10, 10, 10);
}
catch (Exception ex)
{
Console.WriteLine("Не удалось создать треугольник с такими параметрами. Причина: " + ex.Message);
}
Причем ловить это исключение нужно в том месте, где мы готовы его обработать. В данном случае – в основной программе.
Вариант 2. Статический метод
Если конкретная причина почему не удалось создать объект треугольника нас не интересует, то можно добавить статический метод:
public class Triangle
{
..............................
public static Triangle TryCreateTriangle(double x, double y, double z)
{
try
{
return new Triangle(x, y, z);
}
catch
{
return null;
}
}
}
И тогда просто проверяем получилось или нет:
Triangle t1 = Triangle.TryCreateTriangle(10, 10, 10);
if (t1 != null)
{
// создали и можно с ним работать
}
Но это очень редко бывает и в самых простых случаях, типа int.TryParse(). В большинстве случает так делать не стоит, т.к. здесь глушатся совершенно все исключения и найти причину ошибки будет сложно.
Вариант 3. Тип треугольника
Еще вариант – делаем перечисление:
public enum TriangleType
{
Error = 0,
OneMore90 = 1,
OneEqual90 = 2,
AllLess90 = 3
}
В конструкторе проверяем и вычисляем все нужные поля и тип треугольника:
public Triangle(double x, double y, double z)
{
this.x = x;
this.y = y;
this.z = z;
this.type = GetTriangleType();
}
В методе GetTriangleType делаем и проверки и вычисления типа.
А при создании просто проверяем тип – если он не Error, то все хорошо и треугольник какой-никакой, но получился. Т.к. кроме наших собственных исключений у нас больше тут ничего не планируется, то их можно и не делать и обойтись этим вариантом.
Triangle t2 = new Triangle(10, 10, 10);
if (t1.Type == TriangleType.Error)
{
Console.WriteLine("все плохо");
}
Какой вариант выбрать
Выбор варианта зависит от конкретной задачи. Если вам на выбор предложат стол или стул, то первый вопрос будет – “для чего?” Чтобы определиться с конкретной реализацией (здесь я привел всего три варианта, а их можно придумать и больше), нужно понимать в каких условиях это будет работать, в каком месте кода и т.д.
Прежде всего нужна постановка бизнес-задачи. Т.е. задача должна формулироваться с точки зрения пользователя реализуемой программы, а не просто “напишите класс треугольник”. Написать “просто” сложно – слишком много вариантов. Конечно тот вариант, который я показал в начале вообще не проходит. Но и нормальных вариантов реализации очень много.
Что должно происходить, если не удалось создать объект? Просто сообщить об этом пользователю? Попросить пользователя ввести данные повторно? Данные вводит пользователь или они читаются из файла? В файле хранится один треугольник или их список?
Нужен ли вариант с генерацией исключений? Вообще говорят, в таком простом коде – нет. Исключения очень ресурсоемкие и их использование существенно усложняет и замедляет код. Если других исключений, кроме наших собственных не планируется, то можно обойтись и без исключений, например, с помощью варианта с типом треугольника, а причину ошибки, если она есть – хранить в специальном поле.
Если код будет сложнее, например, данные будут читаться из XML или из БД, то возможны другие исключения в этом коде – файл может быть недоступен, иметь неверный формат, в нем могут храниться не числа, числа неверной размерности и т.д. Другими словами, у нас уже есть возможный список исключений и так или иначе, нам придется их обрабатывать. А значит создавать еще один, параллельный механизм смысла не имеет и проще работать с механизмом исключений, добавив к ним свои.
No comments:
Post a Comment