Професионална програма
Loading...
djc_bg2015 avatar djc_bg2015 923 Точки

[Homework] Java OOP - Въпрос относно Problem 1. Geometry

Здравейте,

започнах да пиша домашното към темата, и имам един въпрос относно задача 1:

Ето как съм конструирал проекта до тук:

- абстрактен клас Vertex с наследници Vertex2d/Vertex3d

- абстрактен клас Shape с абстрактни наследници PlaneShape/SpaceShape и всички конкретни форми като наследници

 

Как точно ще стане това абстрактния клас Shape да има лист от Vertex а неговите абстрактни наследници PlaneShape и SpaceShape да имат листове от сътвтно Vertex2d и Vertex3d ? Проблема е че когато добавя полето лист от вектори в Shape заедно с конструктор и после опитам в PlaneShape да преизползвам родителския конструктор , с лист от Vertex2d и ми казва че е невъзможно...

 

Май го написах доста объркващо, но ако някой ме разбере нека пише :)

Тагове:
2
Java Advanced 29/10/2015 22:27:31
Filkolev avatar Filkolev 4485 Точки
Best Answer

Мисля, че проблемът е, че опитваш да запазиш ArrayList<Vertex2d> в поле ArrayList<Vertex>. Поне доколкото рабрах от обяснението ти.

Решението е да си ползваш списъка с абстрактни точки, в конструктора на PlaneShape да приемаш като параметър списък с двуизмерни точки и да ги напълниш ръчно в списъка, който е в родителския клас (вместо да присвояваш целия списък). Направи си обаче някакъв метод/пропърти в Shape, с който наследниците да могат да достъпват и да пълнят списъка на родителя.

1
djc_bg2015 avatar djc_bg2015 923 Точки

Абсолютно си ме разбрал правилно! :)

Благодаря за подсказката - продължавам по задачата.

Поздрави!

0
Filkolev avatar Filkolev 4485 Точки

Надявам се, че си ме разбрал, че не ми е много подробно обяснението. В списък от Vertex може да вкарваш Vertex2D, но в променлива List<Vertex> не може да сложиш List<Vertex2D>, защото второто не е наследник на първото, и двете са списъци. 

0
djc_bg2015 avatar djc_bg2015 923 Точки

Ами разбрах как да реша проблема :)

А сега след втория ти пост, вече разбрах и какъв е проблема. То близко да акъла, но кой д се сети..

А и интелито не помага много .. само ми пишеше - едното в другото не влиза и до там.

Благодаря ти отново!

0
29/10/2015 23:13:18
djc_bg2015 avatar djc_bg2015 923 Точки

Добре, нека обобщя какво съм разбрал:

в класа Shape правя поле List<Vertex> и сетър за това поле за да може да се достъпва от наследниците, без конструктор.. ?

в  абстрактните наследници правя конструктор, който приема лист от точки (2д, 3д) и посредством сетъра на родителя пълни неговия лист

в конкретните наследници, нищо по различно - конструктор който  приема лист от точки ...

 

и съответно, когато искам да създам обект от конкретен тип - например триъгълник, трябва да му подам като аргумент на конструктор Лист от точки ?

 

0
Filkolev avatar Filkolev 4485 Точки

Работиш със списък, това е референтен тип, за да добавяш елементи към него ти трябва гетър, сетър е нужен само ако искаш да смениш обекта.

Това, което си представях, е да има метод в Shape, който взима като параметри някакви точки и ги добавя в списъка. Така в PlaneShape да речем получаваш List<Vertex2D>, подаваш го на наследения метод, който взима точките и ги пълни в полето List<Vertex>. В базовия конструктор просто ще се инициализира списъка, а вече в наследниците ще се вика метода за пълненето му. По-умен начин в момента не се сещам.

1
Innos avatar Innos 419 Точки

Помня я тази задача, един колега от курса тогава го повдигна точно този въпрос и 2 часа се чудехме защо не може, защото на пръв поглед изглежда правилно като полиморфизъм, докато не се усетиш че ти презаписваш целият лист, а не вкарваш елементи в него. Иронията е че може да се счупи и да мине без да те предупреди компилатора, ако използваш трансформиращ метод който връща Generic колекция като Array.asList примерно (не го прави разбира се ).

2
RoYaL avatar RoYaL Trainer 6847 Точки

При Vertex2D extends Vertex && Vertex3D extends Vertex

и

поле List<Vertex> vertices => пропърти List<vertex> getVertices();

можеш да добавяш чрез getVertices().add(new Vertex3D(...));

Ако искаш да подаваш цял лист от други типове, ще трябва да имаш логика, която минава през всеки един от елементите и ги добавя към листа от други елементи, както Фил спомена.

Мисля, че същото нещо може да се постигне и с дженерици? Ако имаш клас, който е дженерик при <V extends Vertex> и имаш лист от същия дженерик (List<V>). Щом веднъж си вигнал класа със V което е Vertex2D то и листът ще стане List<Vertex2D>.

 

1
djc_bg2015 avatar djc_bg2015 923 Точки

Честно да ти кажа, не съм много наясно с ООП-то като цяло. Имах възможност и време да гледам 3-4 лекции от минали курсове и каквото съм прочел из интернет..

така че това със дженерик класа, няма да мога да го направя към момента :)

 

Стигнах до следното решение:

Vertex2D/Vertex3D наследяват Vertex

В класа Shape имам поле List<Vertex> , и следния метод:

    public void addVertices(Vertex... vertices) {
        for (Vertex vertex : vertices) {
            this.vertices.add(vertex);
        }
    }

 

PlaneShape/SpaceShape наследяват Shape, и ето как изглежда PlaneShape класа:

public abstract class PlaneShape extends Shape implements PerimeterMeasurable, AreaMeasurable {
    public PlaneShape(Vertex2D... vertices) {
        super.addVertices(vertices);
    }
}

 

Ето пример и как изглежда конкретен клас, в случая Triangle:

public class Triangle extends PlaneShape {
    private Vertex2D a;
    private Vertex2D b;
    private Vertex2D c;

    public Triangle(Vertex2D a, Vertex2D b, Vertex2D c) {
        super(a, b, c);
        this.a = a;
        this.b = b;
        this.c = c;
    }

....

 

И ето какво се случва в мейн:

public class Main {
    public static void main(String[] args) {

        List<Shape> shapes = new ArrayList<>();
        shapes.add(new Triangle(new Vertex2D(15, 15), new Vertex2D(30,30), new Vertex2D(50,25)));

.....

 

И след овърлоудване на тостринг изхода е нещо подобно:

Circle{area=109,44, perimeter=20,92, Vertices=[Vertex2D{x=16,00, y=18,00}]}
Triangle{area=187,50, perimeter=78,23, Vertices=[Vertex2D{x=15,00, y=15,00}, Vertex2D{x=30,00, y=30,00}, Vertex2D{x=50,00, y=25,00}]}
Rectangle{width=18,90, heigth=77,98, area=1473,82, perimeter=193,76, Vertices=[Vertex2D{x=18,25, y=15,30}]}

 

Има ли нещо вярно в това което съм написал? :)

1
30/10/2015 01:36:39
Innos avatar Innos 419 Точки

Нещата изглеждат ок, само че нагласи accesor-a на getVertices метода да е protected не public, и не мисля че ти трябва да пазиш, точките като полета в триъгълника, нали листа от Vertex-и е за това да ти пази точките, вместо това си направи полета за страните в триъгълника и ги смятай в конструктора, за да не се налага да ги преизчисляваш при всяка операция и конструктора на planeShape го направи protected (конструкторите на абстрактни класове не трябва да са public). Сега като се замисля то по добре направи листа във Shape протектед, ще си спестиш бая проблеми така.

1
30/10/2015 03:18:10
djc_bg2015 avatar djc_bg2015 923 Точки

Благодаря за коментарите!

Относно триъгълника (защото реално , той е единствената форма , която ползва върховете си за пресмятане на Area), ако не му запазя върховете като полета, после при пресмятането на Area, трябва да итерирам през листа, за да вземса стойностите, нали така?

EDIT: Сега заграях, че просто трябва да сменя формулата за пресмятане на лице - да позлват страните а не върховете

ПС. Това ми е метода за пресмятане на лице:

    @Override
    public double getArea() {
        double area = Math.abs((this.a.getX() * (this.b.getY() - this.c.getY()))
                + (this.b.getX() * (this.c.getY() - this.a.getY()))
                + (this.c.getX() * (this.a.getY() - this.b.getY()))) / 2;

        return area;
    }

 

0
30/10/2015 08:00:50
RFilipov avatar RFilipov 136 Точки

И става много интересно в момента, в който искам да направя една проверка при инициализирането на точките на триъгълника => ако са повече от три да хвърля Exception. Не мога да достъпя полето List от клас Shape защото е private немога да го сложа и protected защото в Java нали protected работело по различен начин...трябва да го слагам public което също е тотално грешно.

Може би най-добрият подход е да има само базов конструктор без параметри и отделен метод .AddAll например, който да може да се @Override и там вече да се сложи проверката.

0
30/10/2015 09:37:46
RoYaL avatar RoYaL Trainer 6847 Точки

ОК е да сложиш protected. :-)) Иначе би трябвало да имаш достъп до листът, който ти се подава в конструктура - не можеш ли да провериш него?

0