Упражнения и задачи за по - добро усвояване на OOP

pix3l каза:
Наследяването не е задължително, но е препоръчително да се използва, когато има очевидна връзка между класовете и единия може да допълни другия.

Имам един въпрос: За какво служат празните интерфейси? :D
Те са полезни точно толкова, колкото и тези, в които имаш методи, които трябва да се имплементират. :D

Чакай първо да се уточним. С твоят код ти обясняваш полиморфизъм. Взаимствано от твоят коментар, промених минимално кодат ти за да обясня шаблона фабрика. Нека разграничим първо двете обяснения. Ти наследяваш структурата, аз пък задължавам кода да съдържа метода с дадено име.

За теб класът Animal бива наследяван от подкласовете на Lion, Snake и Еagle и всеки изпълнява метода move. За целта ти правиш инстанция на класовете и един по един ги подаваш, като обекти на тестовият клас, и те стават параметри на метода move. Те задължително трябва да са от наследствената структура на Animal.

Аз имам интерфейс Анимал задължаващ класовете Lion, Snake и Еagle, които го имплементират, да съдържат в себе си метод с наименованието move. При мен се прави инстанция единствено на Тестовият клас и на него подавам имената (string) на класовете, като параметри на метода move. В този метод се решава дали да подаденият стринг е съществуващ клас и дали е имплементирал интерфейса Animal. Ако всичко е наред се създавa инстанция и метода move се изпълнява.

Въпреки, че има видима структура, НЕ е задължително да се използва унаследяване. Ти виждаш последователна структура с клас майка, докато някой друг може да види нуждата единствено от наличието на даден метод с определени параметри. Ти имаш нуждата наследника да изпълнява метод move дали на предшественика, дали негова интерпретация, решението ще е на база логика на приложението ти, докато на друг човек му е нужно този клас да има задължително собствен метод move без да има предшественик. Въпрос на гледна точка, дори различна, тя не е грешна, защото няма стандарт кога кое да се използва, има само препоръки. Всичко зависи от блок схемата на приложението и първоначално създадената структура.

Относно празните интерфейси те са безполезни, също като празните класове майки, които се наследяват. Хората си оставят възможността да задължат имплементиращите класове да имат определен метод използван някъде в приложението, докато добавянето на метод в празен клас майка, пък добавя логиката му, като опция за ползване при всички деца. В структурата с наследяване имаш опцията да използваш логиката на дефаутовият метод или да го презапишеш, докато с интерфейсите ти задължаваш класът просто да има такъв метод, без да даваш на наследниците опцията за дефаутова логика или да те интересува каква логика се съдържа в него.

Мой ред е да задам въпрос. :D :p
Ако метода move трябва да приема два параметъра, как ще задължиш наследниците при extend да използват тези два подадени параметъра? Те откъде ще знаят колко параметъра при презаписване на метода са нужни да приемат или дали да са public, protected и private?

PHP:
<?php

class A  
{
    public function move($empty,$string) 
	{
		if (isset($empty) and isset($string)) {
			print "<br>A ".$empty." ".$string;
		}
    }
}

class B extends A
{
	public function move()
	{
		print "<br>B";
	}
}

class C extends A
{
	
}

(new B)->move("параметър 1","параметър 2");
(new C)->move("параметър 1","параметър 2");

Ето така:

PHP:
<?php
interface I 
{
	public function move($empty,$string);
} 

class A  implements I
{
    public function move($empty,$string) 
	{
		if (isset($empty) and isset($string)) {
			print "<br>A ".$empty." ".$string;
		}
    }
}

class B implements I
{
	public function move()
	{
		print "<br>B";
	}
}

class C implements I
{
	
}

(new A)->move("параметър 1","параметър 2");
(new B)->move("параметър 1","параметър 2");
(new C)->move("параметър 1","параметър 2");

или така

PHP:
interface I 
{
	public function move($empty,$string);
} 

class A  implements I
{
    public function move($empty,$string) 
	{
		if (isset($empty) and isset($string)) {
			print "<br>A ".$empty." ".$string;
		}
    }
}

class B extends A
{
	public function move()
	{
		print "<br>B";
	}
}

class C extends A
{
	
}

(new B)->move("параметър 1","параметър 2");
(new C)->move("параметър 1","параметър 2");


Естествено съм сложил примери и методите им,може да се добавят или премахват или каквото Ви дойде на акъл за теста.
 
Лично аз бих използвал абстрактен метод...
Такъв бих използвал и при Animal, докато пишех примера, но не сметнах за нужно да усложнявам, тъй като целта тук не е да покажем колко знаем. :D

Ето ти пример за полезен празен интерфейс:
Ако трябва да работиш с кода от моя пример, но трябва да изкараш само животните, които могат да летят - ползваш празен интерфейс.

PHP:
interface Flyable {}

class Eagle extends Animal implements Flyable {
    public function move() {
        // Implements flying
    }
}

class FlyTest {
    public function canFly(Animal $animal) {
        if ($animal instanceof Flyable) {
            $animal->move();
        } else {
            // No, this one can't fly...
        }
    }
}

А започнахме с шаблони за дизайн... :D
 
pix3l каза:
Лично аз бих използвал абстрактен метод...
Такъв бих използвал и при Animal, докато пишех примера, но не сметнах за нужно да усложнявам, тъй като целта тук не е да покажем колко знаем. :D

Ето ти пример за полезен празен интерфейс:
Ако трябва да работиш с кода от моя пример, но трябва да изкараш само животните, които могат да летят - ползваш празен интерфейс.

PHP:
interface Flyable {}

class Eagle extends Animal implements Flyable {
    public function move() {
        // Implements flying
    }
}

class FlyTest {
    public function canFly(Animal $animal) {
        if ($animal instanceof Flyable) {
            $animal->move();
        } else {
            // No, this one can't fly...
        }
    }
}

А започнахме с шаблони за дизайн... :D

Поправям се за интерфейсите. И празните са полезни. Въпреки, че съм ползвал подобни неща хич не се усетих (изобщо сетих) за този вид структура сега. :D :?: Както и да е. Наистина от къде тръгнахме, та къде стигнахме. Дано автора не ни се сърди за обясненията :p :oops:
 
Това е техническа дискусия, не говорим празни приказки и всеки може да научи нещо. :D
 
pix3l каза:
Това е техническа дискусия, не говорим празни приказки и всеки може да научи нещо. :D
Хехехехе съгласен съм. :?: Веднага драскам код над който автора да се замисли и сам за себе си да си отговори, защо се ползва интерфейса в конкретният случай и с какво той въздейства на класовете extends (наследяващи) Animal. (Нещо като задачка за размисъл му давам) :p

PHP:
<?php
interface I 
{
	public function move($option);
}

abstract class Animal implements I
{
    public function move($option) 
    {
        print "<br>".get_called_class()." ".$option;
    }
}

class Snake extends  Animal 
{
	
}

class Eagle extends  Animal 
{
 
}

class Lion  extends Animal
{
	public function move($option)
	{
		switch ($option) {
			case 1 :
				$h = "Runnig Slow";
				break;
			case 2 :
				$h = "Runnig Fast";
				break;
				default : 
					$h = "Sleep";
		}
		print "<br>".get_called_class()." ".$h;
	}
}
        
class Driver 
{
    public function move($animalName,$option) 
    {	
        $err = "";
        if (class_exists($animalName)) {
            $classAnimal = new $animalName;
            if ($classAnimal instanceof I) {
                $classAnimal->move($option);
            } else {
                $err = "Not instance!";
            }
        } else {
            $err = "$animalName no exist!";
        }
        if (!empty($err)) {
            print "<br>".$err;
        } 
    }
}

$driver = new Driver();

$driver->move("Snake","Creeping");
$driver->move("Eagle","Flying");
$driver->move("Lion",1); 
$driver->move("Lion",2); 
$driver->move("Lion",""); 
$driver->move("Fish","Swimming");
 
За това хората са казали Favor Composition over Inheritance. Но това е advanced тематика, която пак казвам, не е време да научи.

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

Не става въпрос да му се обясни сингълтон и фактори шаблоните, защото някои шаблони не решават конкретни проблеми. Когато му дойде реда, трябва да се запознае с всеки или поне с повечето. Всеки език набляга на конкретни шаблони, които се използват най-много и са най-полезни.

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

И за да поотмажа положението - шаблоните за дизайн се използват умно, а не безразборно. Не значи, че трябва да се използват навсякъде.
 
Дали ще може малко помощ, тъй като го закъсах.. ;д сигурен съм, че е нещо елементарно, но аз не го виждам. Реших да направя Routes по урок, но се мъча вече цял ден без успех и с куп грешки .. Последната, на която се запънах е тази
Fatal error: Cannot declare class Router, because the name is already in use in F:\xampp\htdocs\routers.php on line 9


index.php

PHP:
<?php
/**
 * Created by PhpStorm.
 * User: Melomanchetoo
 * Date: 7.3.2017 г.
 * Time: 22:58
 */


require 'routers.php';


$router = new Router;

require 'routesPath.php';

$uri = trim($_SERVER['REQUEST_URI'], '/');

var_dump($uri);

require $router -> direct($uri);


routesPath.php

PHP:
<?php
/**
 * Created by PhpStorm.
 * User: Melomanchetoo
 * Date: 7.3.2017 г.
 * Time: 23:07
 */

$router -> define([
    '' => 'index.php',
    'about' => 'about.php',
    'contact' => 'contact.php'
]);

routers.php


PHP:
<?php

/**
 * Created by PhpStorm.
 * User: Melomanchetoo
 * Date: 7.3.2017 г.
 * Time: 22:59
 */
    class Router
    {

        protected $routes = [];
        protected $uri;


        public function define($routes)
        {

            $this -> routes = $routes;


        }

        public function direct($uri)
        {

            $this -> uri = $uri;

            if(array_key_exists($uri, $this->routes)){

                return $this -> routes[$uri];


            }

        }
    }
 
Сложи едно echo "Test"; в routers.php за да видиш колко пъти се зарежда този файл. (да не би да го инклудваш втори път някъде другаде)
Пробвай и временно да смениш името, например на Routers, и пробвай пак.
 
Когато извикаш index-а, $_SERVER приема стойността на индекса и де факто се опитваш да рикуарнеш отново индекс и за това ти казва, че вече имаш инстанция на класа.

Когато дефинираш рутера, не трябва да добавяш и index-а, защото той ти играе роля на диспечер.


PHP:
<?php
/**
 * Created by PhpStorm.
 * User: Melomanchetoo
 * Date: 7.3.2017 г.
 * Time: 22:58
 */


require 'routers.php'; // зареждаш класа


$router = new Router; // правиш инстанция

require 'routesPath.php'; // дефинираш пътищата
 
$uri = trim($_SERVER['REQUEST_URI'], '/'); // присвояваш index.php

var_dump($uri);

// докато си в index.php се опитваш да заредиш отново
// index.php и в случая кода се наслагва
require $router -> direct($uri);

Сложих коментари.
 
Revelation каза:
Когато извикаш index-а, $_SERVER приема стойността на индекса и де факто се опитваш да рикуарнеш отново индекс и за това ти казва, че вече имаш инстанция на класа.

Когато дефинираш рутера, не трябва да добавяш и index-а, защото той ти играе роля на диспечер.


PHP:
<?php
/**
 * Created by PhpStorm.
 * User: Melomanchetoo
 * Date: 7.3.2017 г.
 * Time: 22:58
 */


require 'routers.php'; // зареждаш класа


$router = new Router; // правиш инстанция

require 'routesPath.php'; // дефинираш пътищата
 
$uri = trim($_SERVER['REQUEST_URI'], '/'); // присвояваш index.php

var_dump($uri);

// докато си в index.php се опитваш да заредиш отново
// index.php и в случая кода се наслагва
require $router -> direct($uri);

Сложих коментари.

Как би ме посъветвал да го направя?
 
Ами да не използваш диспечера в описанието рутове. Т.е. всичко що мачне индекса, го пропускаш.
 
Ако в главната директория ти стои index.php, то сложи всички страници в директория pages, да речем, и тогава вътре в нея ще имаш скриптове за отделните страници. Тогава при require ще имаш require "pages/". $route->load(..) и няма да става рекурсивно инклудване.

/
/index.php - entry point за всеки request. Оттук инклудваш страниците в pages/
/router.php
/pages
/pages/index.php - главната страница
/pages/about.php - други страници

В /index.php ще имаш

require "pages/" . $router -> direct($uri);

а в /pages/index.php ще ти стои главната страница на сайта.

Ако не искаш да са в отделна дир. може и да прекръстиш index.php на main.php.
 

Горе