У нас есть общий метод
FindPerimetr()
для классов
Rectangle
и Square
.
Как реализовать так чтобы не писать метод два раза в разных классах? Слышал что то про родительский класс это оно?
К сожалению, вам придется реализовать этог метод дважды, т.к. его реализация отличается для квадрата и для прямоугольника.
public abstract class Polygon
{
public abstract double FindPerimetr();
}
public class Square : Polygon
{
public double Size { get; set; }
public override double FindPerimetr()
{
return Size * 4;
}
}
public class Rectangle : Polygon
{
public double Width { get; set; }
public double Height { get; set; }
public override double FindPerimetr()
{
return (Width + Height) * 2;
}
}
Вариант с интерфейсом вместо abstract class
тоже подойдет.
Вам будут советовать унаследовать квадрат от прямоугольника - но такое наследование нарушит принцип подстановки Барбары Лисков. А нарушать принципы из списка SOLID нехорошо. Например, вы скажете "квадрат - это такой прямоугольник, у которого ширина равна высоте". И напишете вот такой код:
public class Square : Rectangle
{
public override double Width { get => base.Width; set => base.Width = base.Height = value; }
public override double Height { get => base.Height; set => Width = value; }
}
public class Rectangle
{
public virtual double Width { get; set; }
public virtual double Height { get; set; }
public double FindPerimetr()
{
return (Width + Height) * 2;
}
}
Плохой препод по ООП скажет "молодец, ты проявил знание наследования!". Хороший препод по ООП скажет: Смотри, у нас был вот такой метод Stretch(Rectangle r)
, который растягивал прямоугольник в 2 раза:
static void Stretch(Rectangle r)
{
r.Height *= 2;
r.Width *= 2;
}
И этот метод отлично работает до тех пор, пока не в него не попадет квадрат. А квадрат он, внезапно, сделает длинее и шире в 4 раза.
Добавлю к ответу PashaPash еще два способа унаследовать квадрат от прямоугольника, которые не нарушают LSP.
Первый способ - можно сделать их неизменяемыми:
public class Rectangle
{
public double Width { get; }
public double Height { get; }
public Rectangle (double w, double h)
{
Width = w;
Height = h;
}
public double FindPerimetr() => (Width + Height) * 2;
}
public class Square : Rectangle
{
public Square (double size) : base(size, size) { }
}
Способ второй - оставить их изменяемыми, но не давать прямого контроля над значениями свойств:
public class Rectangle
{
public double Width { get; protected set; }
public double Height { get; protected set; }
public Rectangle (double w, double h)
{
Width = w;
Height = h;
}
public double FindPerimetr() => (Width + Height) * 2;
public void Stretch(double s)
{
Width *= s;
Height *= s;
}
}
public class Square : Rectangle
{
public Square (double size) : base(size, size) { }
}
Но надо заметить, что второй пример на самом деле соблюдает LSP ценой жертвы OCP (принципа открытости-закрытости): любая операция, добавленная в базовый класс, должна учитывать все его подклассы, а неучтенные заранее подклассы оказываются при таком подходе запрещены.
К примеру, если бы в класс Rectangle
был добавлен метод void Stretch(double sx, double sy)
- то его наличие уже не позволило бы унаследовать класс Square
от Rectangle
.
Тем не менее, OCP не так важен как LSP - и при построении, к примеру, DSL (языков специфичных для предметной области) такой подход может быть оправдан.
Для любой плоской фигуры из 4-х точек периметром будет сумма длин отрезков их соединяющих. Если расчет периметра реализовать в абстрактном классе - реализовывать в наследниках не потребуется.
using System;
namespace Perimeter
{
public class Point
{
public double X { get; set; }
public double Y { get; set; }
public Point(double X, double Y)
{
this.X = X;
this.Y = Y;
}
}
public abstract class Tetragon
{
public Point Vertex1 { get; set; }
public Point Vertex2 { get; set; }
public Point Vertex3 { get; set; }
public Point Vertex4 { get; set; }
public Tetragon(Point Vertex1, Point Vertex2, Point Vertex3, Point Vertex4)
{
this.Vertex1 = Vertex1;
this.Vertex2 = Vertex2;
this.Vertex3 = Vertex3;
this.Vertex4 = Vertex4;
}
public double FindPerimetr()
{
double Length1 = Math.Sqrt(Math.Pow(Vertex2.X - Vertex1.X, 2) + Math.Pow(Vertex2.Y - Vertex1.Y, 2));
double Length2 = Math.Sqrt(Math.Pow(Vertex3.X - Vertex2.X, 2) + Math.Pow(Vertex3.Y - Vertex2.Y, 2));
double Length3 = Math.Sqrt(Math.Pow(Vertex4.X - Vertex3.X, 2) + Math.Pow(Vertex4.Y - Vertex3.Y, 2));
double Length4 = Math.Sqrt(Math.Pow(Vertex1.X - Vertex4.X, 2) + Math.Pow(Vertex1.Y - Vertex4.Y, 2));
return Length1 + Length2 + Length3 + Length4;
}
}
public class Rectangle : Tetragon
{
public Rectangle(double width, double height)
: base(new Point(0, 0), new Point(0, height), new Point(width, height), new Point(width, 0))
{ }
}
public class Square : Tetragon
{
public Square(double size)
: base(new Point(0, 0), new Point(0, size), new Point(size, size), new Point(size, 0))
{ }
}
class Program
{
static void Main(string[] args)
{
// 10
var rec = new Rectangle(2, 3);
Console.WriteLine(string.Format("Perimeter of rectangle: {0}", rec.FindPerimetr()));
// 4
var square = new Square(1);
Console.WriteLine(string.Format("Perimeter of square: {0}", square.FindPerimetr()));
Console.ReadLine();
}
}
}
Вариантов много. Незнаю нужно ли учить плохому , но я бы выделил:
Покажу из них два.
"дружбу классов"
public double FindPerimetr() {
if (this is Square) return ((Square)this).Size *4;
if (this is Rectangle)
return (((Rectangle)this).Width + ((Rectangle)this).Height) * 2;
return 0; // неизвесно что
}
Недостаток, тот кто будет наследовать ваш класс - будет недоволен что его периметр не считается. Если "правильно" расставить if-последовательность и одинаково назвать одну из сторон - можно обойтись двумя классами.
Можно "схитрить" через рефлексию
using System.Reflection; // рефлексия
using System.ComponentModel; // тут AmbientValue
public class Polygon {
public double FindPerimetr() {
double ret = 0;
foreach (PropertyInfo item in this.GetType().GetProperties())
if (item.PropertyType == typeof(double)) // Проверки
ret += (double) item.GetValue(this,null);
return ret;
}
}
Метод будет работать если каждая сторона "присутствует". Если нужно множители - их нужно где-то прописать, например в атрибутах.
Например так:
public class Polygon {
public double FindPerimetr() {
double ret = 0;
foreach (PropertyInfo item in this.GetType().GetProperties())
if (item.PropertyType == typeof(double)) // Проверки
{
object[] attr = item.GetCustomAttributes(typeof(AmbientValueAttrubute,false);
ret += ((double) item.GetValue(this,null))
* ((attr.Length==0)?0: (double) ((AmbientValueAttrubute)attr[0]).Value);
}
return ret;
}
}
public class Rectangle : Polygon {
[AmbientValue(4)]
public double Size { get; set;}
}
Но лучше для атрибута создать свой класс на базе Attribute. Рефлексия не считается хорошим тоном, но как вариант. Она сложная, чуть замедляет работу и плохочитабельна. Про доп-атрибут тому кто будет продолжать дорабоатывать вашу библиотеку прийдётся тоже догадываться.
А вот стоит ли "овчина выделки" - судите сами.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Собственно есть строка "stackoverflow", нужно её разбавить точками(подскажите алгоритм), вот пример:
Сортировка списка с учетом регистра listSort() - C#
Я пытался создать автообновление своей программы через zip-архивДля этого использую DotNetZip (Ionic
Здравствуйте! Недавно столкнулся с проблемой скачивания картинки с сайта litresru