проектирование класса формы с кругом и треугольником

Я пытаюсь понять отношения типа «это против того, что есть», где я где-то читал, что мы должны стараться следовать дизайну так, чтобы у нас всегда были отношения — а не «как». Рассмотрим классический пример базового класса формы и производных классов треугольника и круга. круг — это доля, а треугольник — форма. Область отображения функций была определена в базовом классе. Теперь программа ниже работает нормально.

#include "stdafx.h"#include<cmath>
#include <iostream>

class shape
{
public:
virtual void displayArea()=0;
};class circle :public shape
{
int radius;
public:
circle(int radius2) :radius(radius2){  }
void displayArea()
{
double area = 3.14*radius*radius;
std::cout << " \n Area circle" << area<<std::endl;
}
};

class triangle :public shape
{
double a,b,c;
public:
triangle(double a1, double b1, double c1): a(a1), b(b1),c(c1)
{
if (a + b > c && a + c > b && b + c > a)
std::cout << "The sides form a triangle" << std::endl;
else
std::cout << "The sides do not form a triangle. Correct me !" << std::endl;

}

void displayArea()
{double s = (a + b + c) / 2;
double area = sqrt(s*(s - a)*(s - b)*(s - c));
std::cout << " \n Area triangle"<< area<<std::endl;
}
};

void main()
{
shape * p1[2];
p1[0]= new circle(20);

p1[1] = new triangle(5.6,8.1,10.3);
for (int i = 0; i < 2; ++i)
{
p1[i]->displayArea();
}

int y;
std::cin >> y;
}

Теперь, если требование приходит, нужно реализовать modifyShape функция, в которой каждый параметр формы изменяется в зависимости от параметра пользователя, а затем, как я должен изменить свои классы так, чтобы мои отношения is-a не изменились. Когда я смотрю на это, я чувствую, что мне придется определить один аргумент modifyShape в кругу и 3 аргумента modifyShape в треугольнике. Но как эта функция должна выглядеть в базовом классе?

Вариант 1: я определяю как один аргумент, так и два аргумента modifyShape функция в форме, но это означало бы, что у меня будет дополнительная функция с 2 аргументами в круге и дополнительная функция с 1 аргументом в треугольнике.

Вариант 2: я определяю функцию переменного аргумента modifyShape в форме, но почему-то это не выглядит чище для меня.

2

Решение

Существует третий вариант, который вы можете использовать, вы можете создать новую иерархию классов
(или структуры), которые будут представлять параметры каждой фигуры. Тогда вы можете пройти
указатель на базовый класс в качестве аргумента виртуальной функции.
Например:

struct ShapeParams
{
...
}

struct TriangleParams : public ShapeParams
{
double a;
double b;
double c:
}
class shape
{
public:
virtual void displayArea()=0;
modifyShape (ShapeParams*) = 0;
};

class triangle :public shape
{
public:
void modifyShape (ShapeParams*) = override;

private:
TriangleParams m_params;
}
0

Другие решения

Вы можете немного реструктурировать свои классы, но для этого потребуется другой независимый класс. Вы можете создать набор двухмерных и трехмерных математических векторных классов, но вам нужно будет иметь все перегруженные операторы и математические функции, которые могут выполнять векторы, такие как сложение, вычитание, умножение на вектор или на скаляр, а если на вектор у вас есть точка и перекрестный продукт, чтобы беспокоиться о. Вам нужно будет нормализовать методы, длину и тому подобное. Как только у вас есть эти рабочие классы векторной математики. Затем вы можете переделать ваши классы фигур, используя вместо этого векторы. Или вместо написания собственного векторного класса вы можете использовать класс математической библиотеки, такой как математическая библиотека GLM, которая используется для работы в OpenGL. Это бесплатный и открытый исходный код, а также библиотека только для заголовков. После установки библиотеки по пути все, что вам нужно сделать, это включить ее заголовок. Вам не нужно беспокоиться о ссылках. Тогда с этими векторными классами это облегчит выполнение математики в ваших классах фигур, и будет проще создавать классы фигур: Вот пример того, как будет выглядеть псевдокод:

#include <glm\glm.hpp>
// Needed If Doing Matrix Transformations: Rotation, Translation Scaling etc.
// #include <glm\gtc\matrix_transform.hpp>

class Shape {
public:
enum Type {
NONE = 0,
TRIANGLE,
SQUARE,
CIRCLE,
};
protected:
Type type_;
glm::vec4 color_ { 1.0f, 1.0f, 1.0f, 1.0f }; // Initialize List Set To White By Default
double perimeter_; // Also Circumference for Circle
double area_;
// double volume_; // If in 3D.
public:
// Default Constructor
Shape() : type_( NONE ), color_( glm::vec4( 1.0f, 1.0f, 1.0f, 1.0f ) ) {}
// User Defined Constructors
// Sets Shape Type Only Color Is Optional & By Default Is White
explicit Shape( Type type, glm::vec4 color = glm::vec4() ) : type_(type), color_( color ) {}

Type getType() const { return type_; }
void setType( Shape::Type type ) {
if ( type_ == NONE ) {
// Its okay to set a new shape type
type_ = type;
}

// We Already Have a Defined Shape
return;
}

// Getters That Are Commonly Found Across All Shapes
double getPerimeter() const { return perimeter_; }
double getArea() const { return area_; }

// Common Functions that can be done to any shape
void setSolidColor( glm::vec4 color ) { color_ = color };
glm::vec4 getColor() const { return color; }

// Common Interface That All Shapes Share But Must Override
virtual double calculateArea() = 0;
virtual double calculatePerimeter() = 0;

// Since we do not know what kind of shape to modify until we have one
// to work with, we do not know how many parameters this function will need.
// To get around this we can use a function template and then have overloads
// for each type we support
template<typename Type = Shape>
virtual void modify( Type* pShape /*,glm::vec3... params*/ );

// Overloaded Types: - Should Be Defined & Overridden By the Derived Class
virtual void modify<Triangle>( Triangle* pTriangle, glm::vec3, glm::vec3, glm::vec3, glm::vec4 = glm::vec4() ) { /* ... */ }
virtual void modify<Circle>( Cirlce* pCircle, float radius, glm::vec4 color = glm::vec4() ) { /* ... * / }

};

Тогда унаследованный класс будет выглядеть примерно так:

class Triangle : public Shape {
public:
// Could Be An Option To Where This is a base class as well to specific types of triangles:
enum TriangleType {
Acute = 0,
Right,
Equilateral,
Obtuse
} // then each of these would have properties specific to each type
private:
glm::vec3[3] vertices_;

public:
// Default Constructor
Triangle() : Shape(TRIANGLE) {} // Sets The Shape Type But Has No Vertices Or Area; just default construction
// Vertices But No Color
Triangle( glm::vec3 A, glm::vec3 B, glm::vec3 C ) : Shape(TRIANGLE) {
vertices_[0] = A;
vertices_[1] = B;
vettices_[2] = C;

// Call These To Have These Values
calculatePerimeter();
calculateArea();
}
// Vertices & Color
Triangle( glm::vec3 A, glm::vec3 B, glm::vec3 C, glm::vec4 color ) : Shape(TRIANGLE) {
vertices_[0] = A;
vertices_[1] = B;
vertices_[2] = C;

calculatePerimeter();
calculateArea();
}

// No Need To Do The Set & Get Colors - Base Class Does that for you.

// Methods that this shape must implement
virtual double calculateArea() override {
// Calculations For Getting Area of A Triangle
area_ = /* calculation */;
};
virtual double calculatePerimeter() override {
// Calculations For Getting Perimeter of A Triangle
perimeter_ = /* calculation */;
};

void modify<Triangle>( Triangle* pTriangle, glm::vec3, glm::vec3, glm::vec3, glm::vec4 = glm::vec4() ) override { /* ... */ }

};

Теперь что касается отображения информации; лично я не реализовал бы это в этих классах. Просто используйте ваш стандарт std::cout или же std::ofstream и т.д., чтобы распечатать значения на экране или в файле, просто купите, используя getters такие как это:

#include <iostream>
#include "Triangle.h"
int main() {
Triangle t1( glm::vec3( 0.0f, 1.0f, -1.3f ),   // Vertex A
glm::vec3( 3.2f, 5.5f, -8.9f ),   //        B
glm::vec3( -4.5f, 7.6f, 8.2f ),   //        C
glm::vec4( 0.8f, 0.9f, 0.23f, 1.0f ) ); // Color

std::cout << "Perimeter is " << t1.getPerimeter() << std::endl;
std::cout << "Area is " << t1.getArea() << std::endl;

return 0;
}
0

По вопросам рекламы [email protected]