Jak zastosować koncepcje OOP do budowy prostej, ale rzeczywistej aplikacji internetowej? [Zamknięte]

25

Od dłuższego czasu próbuję owinąć głowę wokół OOP. Widzę jego zalety. Przeczytałem wiele tutoriali i obejrzałem tyle samo filmów na ten temat. Dostaję przykłady zwierząt / kotów / psów, dostaję przykłady samochodów / jazdy. Walczę z tym, jak zastosować te koncepcje w rzeczywistej aplikacji. Tak więc postanowiłem zbudować jeden przy użyciu OOP.

Nie proszę o pomoc w składni lub pisaniu konkretnego kodu - mogę to znaleźć w dokumentacji i przeszukując fora itp. Tak naprawdę potrzebuję wskazówek i od czasu do czasu popychania we właściwym kierunku. Czy są doświadczeni programiści, którzy chcą mnie mentorować?

Jako mój projekt edukacyjny chciałbym zbudować prostą „aplikację internetową” ogłoszeń. Coś podobnego do Craigslist, ale znacznie rozwodnione pod względem zakresu. Chciałbym używać PHP5 i MySQL, ponieważ jestem z nimi zaznajomiony.

Powiedzmy, że są tylko 2 przypadki użycia:

  1. Zamieszczanie czegoś na sprzedaż
  2. Przeglądanie / szukanie czegoś do kupienia

Jakie „rzeczy” powinny być przedmiotami? Mogę sobie wyobrazić, że każdy przedmiot może być przedmiotem, ale w którym momencie? I dlaczego?

Na przykład użytkownik wypełnia formularz „wystawić na sprzedaż”, czy formularz ten powinien zostać przekształcony w obiekt, który jest przekazywany do innego obiektu, który wstawia wartości do bazy danych?

A co, gdy inny użytkownik przegląda i prosi o wyświetlenie wszystkich pozycji kategorii C? Czy ma sens, że ilekroć aplikacja musi połączyć się z bazą danych, tworzy obiekt bazy danych, a następnie pobiera kilka obiektów przedmiotów i wyświetla je na stronie? … Napisanie tego z pewnością uświadamia mi, jak wciąż nieświadomy jestem OOP. Proszę, pomóż mi to naprawić.

Jeśli Twoim zdaniem nie jest to dobry projekt, aby rozpocząć branie w OOP, możesz zaproponować inny pomysł!

bernk
źródło
1
Jestem na tej samej łodzi, myślę, że rozumiem OOP - minęło trochę czasu, odkąd próbowałem Java, ale jeśli chodzi o PHP, wiedziałbym, jak zrobić takie rzeczy natychmiast „normalnie”, ale jeśli chodzi o myślenie, jak to zrobić byłoby to zrobione przy użyciu OOP. Tracę wolę życia.
martincarlin87
Forma nie jest przekształcana w obiekt. Obiekt jest instancją klasy. Możesz to zobaczyć w ten sposób. $ item-> saveItem ($ _ POST ['name'], $ _POST ['description']); edytuj To, co naprawdę pomogło mi zrozumieć OOP, to stworzenie prostej aplikacji internetowej „księgi gości”. Niech użytkownicy logują się, publikują wiadomości, edytują wiadomości, usuwają wiadomości i wyszukują wiadomości itp.
@pduersteler dobry pomysł, jak mam to zrobić? Trzeba przyznać, że to moje pierwsze pytanie dotyczące stackoverflow :)
@Bono Być może aplikacja księgi gości, o której wspomniałeś, jest naprawdę lepszym miejscem do rozpoczęcia. Drugą, o której myślałem, była bardzo prosta aplikacja do listy, w której użytkownicy logują się, tworzą / edytują / usuwają listy, dodają / edytują / usuwają elementy z tych list. Czy miałbyś coś przeciwko udostępnieniu nam swojej księgi gości?
Nie miałbym nic przeciwko dzieleniu się nim, choć byłoby to trochę kodu do opublikowania. Mógłbym podzielić się z tobą prostą przykładową klasą, jeśli chcesz. Nie wiem też, jak dobrze ten kod będzie działał, bo szczerze mówiąc minęło już trochę czasu: P Zamieszczę go poniżej

Odpowiedzi:

17

Szczerze sądzę, że jak dotąd porady dla nowych uczniów z OO były okropne. Nie jest dobrym pomysłem, aby od razu zacząć myśleć o obiektach jako o reprezentacji określonego wystąpienia „rzeczy” zdefiniowanej przez jakąś klasę. Lepiej myśleć o nich jako o podzielonych na części komponentach maszyny, które mają pewne interakcje między sobą, ale nie są wewnętrznymi elementami. Każdy z tych składników utrzymuje stan

Jeśli chcesz używać ORM (mapowanie obiektowo-relacyjne) do interakcji DB, niezależnie od tego, jakiego frameworku używasz lub tworzysz, prawdopodobnie będzie miał kilka płytkich obiektów reprezentujących tabele, które prawdopodobnie są kolekcjami „rzeczy”, ale osobiście nie lubię ORM , i nie sądzę, że niekoniecznie reprezentują idealne praktyki OO, ale są popularne w dużych aplikacjach internetowych.

Poza tym prawdopodobnie będziesz mieć pewne ważne komponenty, które musi uruchomić komputer z aplikacją, takie jak jedno lub więcej połączeń DB (możesz stworzyć klasę, która utrzymuje połączenie i możesz uruchamiać przygotowane zapytania - PDOjest świetny , ale owinąłbym to) i być może system szablonów dla twoich widoków. Możesz także chcieć, aby twoje kontrolery były również obiektami PHP. Jeśli masz formularz do wypełnienia, możesz mieć obiekt, który zachowuje wartości formularza dla P / R / G, token zabezpieczający CSRF i może wykonywać weryfikację na swoich danych wejściowych.

Nie powinieneś próbować szukać „rzeczy”, które zamieniają się w obiekty podczas konstruowania projektu aplikacji i grafu obiektowego. Zamiast tego powinieneś pomyśleć o logicznych komponentach, które tworzą je razem. Nie sądzę, że powinieneś próbować to wymusić, i powinno to nastąpić dość naturalnie, ale bardzo trudno to zrobić poprawnie i na pewno skończysz z koniecznością zmiany niektórych decyzji projektowych po drodze.

Moja ostatnia rada jest następująca: kompozycja zamiast dziedziczenia jest właściwą drogą.

Pigułki przeciwwybuchowe
źródło
Zasadą, którą mam, szczególnie dla języków dynamicznych, jest próba tworzenia klas tylko wtedy, gdy chcę skorzystać z polimorfizmu (to znaczy, jeśli klasy te zaimplementują różne wersje tej samej metody, a logika będzie zależeć od tego jakoś). W przeciwnym razie staram się pisać w bardziej „proceduralnym” stylu, aby było to proste.
hugomg
9

Oto, w jaki sposób możesz używać OOP do kupowania i sprzedawania swoich zwierząt domowych, tę samą metodologię można zastosować do sprzedaży samochodów lub samolotów; p

<?php
// define a superclass .. no instances will be made of 'animal' itself,
// but it is useful to define common characteristics and behaviours
// (ie: properties and methods) of all our classes of animals
class Animal {

    // this constructor function is called whenever a new instance
    // of the Animal class is created (or any class that inherits from Animal)
    function Animal ($colour) {

        // install the argument as an attribute of any instances of Animal
        $this->colour = $colour;
    }

    // this method will be available to all classes that inherit from Animal
    function report () {
        return "This ".$this->colour." ".get_class($this)." has ".$this->legs." legs.<br />";
    }
}

// this class inherits from Animal
class Cat extends Animal {

    // set the legs attribute
    public $legs = 4;

    // create a method that can be called from any instances of Cat
    function make_noise () {
        echo "MEOW!<br />";
    }
}

// this class inherits from Cat, and from Animal
class Panther extends Cat {

    // specifies the colour attribute
    public $colour = "black";

    // overwrites the constructor function that would otherwise be
    // inherited from Animal, with a blank constructor.
    function Panther () {}

    // overwrites the method inherited from Cat
    function make_noise () {
        echo "ROARRRR!<br />";
    }
}

// this class inherits from Animal
class Snake extends Animal {
    public $legs = 0;
}

// this class is unrelated to the others
class PetShop {

    // set up an array to store the pets that the shop will stock
    public $pets = array ();

    // set up a variable to store the total cash in the pet shop
    public $cash;

    // this method creates a new object and adds it to the pets array
    function add_pet ($petclass, $price, $colour) {

        // set up a variable containing the number of elements in the pets array
        $n_pets = count($this->pets);

        // add to the pets array, a new instance of the class specified as
        // the first argument in this method, using the last argument as the
        // colour argument that is passed to the specified class's constructor
        $this->pets[$n_pets] = new $petclass($colour);

        // add a 'price' attribute to the pet object
        $this->pets[$n_pets]->price = $price;
    }

    // this method removes the specified pet from the array and adds the price
    // to the pet shop's cash variable
    function sell_pet ($n) {

        // add pet's price to the cash total
        $this->cash += $this->pets[$n]->price;

        // remove the pet object from the array
        array_splice($this->pets, $n, 1);

        // give a message about the sale
        echo "SALE: Pet no. ".$n." sold. Total cash is now \$".$this->cash.".<br /><br />";
    }

    // this method reports on the pet shop's stock
    function show_pets () {

        // show the number of pets available
        echo "<B>Shop stock:</B><br />We have ".count($this->pets)." pets for sale.";
        echo "<br /><br />";

        // iterate through the pets array and show information about each one
        for ($i = 0; $i < count($this->pets); $i++) {
            echo "<B>Pet No. ".$i.": </b>".$this->pets[$i]->report();
            echo "Price: \$".$this->pets[$i]->price."<br />";
        }
        echo "<br />";
    }
}

// instantiate a new PetShop object
$shop = new PetShop ();

// add three pets to the shop
$shop->add_pet(cat, 20, "tabby");
$shop->add_pet(snake, 40, "brown");
$shop->add_pet(snake, 60, "black");

// show the pet's stock
$shop->show_pets();

// sell the first pet in the stock
$shop->sell_pet(0);

// show the pet's stock after the sale
$shop->show_pets();
?>
Lawrence Cherone
źródło
28
Jeśli zobaczę jeszcze jeden przykład z samochodami lub zwierzętami, zgubię go
Neil McGuigan
5

Na prośbę OP podzielę się moim kodem księgi gości.
Klasa wiadomości:

<?php 
Class message
{
    private $db;
    private $messageID;
    private $message;
    private $name;
    private $mail;

    public function setmessageID($messageID)
    {
        $this->messageID = $messageID;
    }

    public function getmessageID()
    {
        return $this->messageID;
    }

    public function setmessage($message)
    {
        $this->message = $message;
    }

    public function getmessage()
    {
        return $this->message;
    }

    public function setname($name)
    {
        $this->name = $name;
    }

    public function getname()
    {
        return $this->name;
    }

    public function setMail($mail)
    {
        $this->mail = $mail;
    }

    public function getMail()
    {
        return $this->mail;
    }
}

Klasa obiektu danych dostępu do wiadomości:

<?php 
class messageDAO
{
    private $db;
    private $aantalMessages;
    private $messages;
    private $message;

    //bij laden roept hij automatisch Db class aan (en de daarbij gezeten functies)
    public function __construct(Db $db)
    {
        $this->db = $db;
    }

    public function getMessages()
    {
        return $this->messages;
    }

    public function getAantalMessages()
    {
        return $this->aantalMessages;
    }

    //Function to retrieve messages
    public function findMessages($args)
    {       
        $dbh = $this->db->DBH();

        //$offset for pagination
        $offset = ($args['currentPage'] - 1) * $args['itemsPerPage'];

        $sth = $dbh->prepare("SELECT    SQL_CALC_FOUND_ROWS
                                                    messageen.messageID, 
                                                    messageen.message, 
                                                    messageen.name, 
                                                    messageen.mail
                                            FROM    `messageen` 
                                            ORDER BY messageen.datumToegevoegd DESC 
                                            LIMIT   ?, ?");
        $sth->bindParam(1, $offset, PDO::PARAM_INT);
        $sth->bindParam(2, $args['itemsPerPage'], PDO::PARAM_INT);
        $sth->execute();
        $sth->setFetchMode(PDO::FETCH_ASSOC);

        $messages = array();

        while($row = $sth->fetch())
        {
            $message = new message();
            $message->setMessageID(htmlentities(strip_tags($row['messageID'])));
            $message->setSessage(htmlentities(strip_tags($row['message'])));
            $message->setName(htmlentities(strip_tags($row['name'])));
            $message->setMail(htmlentities(strip_tags($row['mail'])));  
            $messages[] = $message; 
        }

        $sth = $dbh->prepare("SELECT FOUND_ROWS() as numberOfMessages");
        $sth->execute();
        $sth->setFetchMode(PDO::FETCH_ASSOC);
        $this->numberOfMessages = $sth->fetch();

        return $messages;
    }

    public function setMessageToEdit($args)
    {   
        $sth = $this->db->DBH()->prepare("SELECT    messages.message
                                            FROM    `messages`
                                            WHERE   messages.messageID = ?");
        $sth->bindParam(1, $args['messageID']);
        $sth->execute();
        $sth->setFetchMode(PDO::FETCH_ASSOC);
        //return the retrieved message
        while($row = $sth->fetch())
        {
            $message = new message();
            $message->setMessage(htmlentities(strip_tags($row['message'])));
            $message->setMessageID(intval($args['messageID']));
        }

        return $message;
    }

    //functie om messageen aan te passen
    public function save(message $message)
    {   
        //insert part
        //if(isset($message->getname()) && isset($message->getmessage()) && isset($message->getMail()))
        //{
            $sth = $this->db->DBH()->prepare("INSERT INTO   `messages`
                                                    SET     messages.name = ?,
                                                            messages.mail = ?,
                                                            messages.message = ?,
                                                            messages.dateAdded = NOW()");
            $sth->bindParam(1, $message->getName());
            $sth->bindParam(2, $message->getMail());
            $sth->bindParam(3, $message->getMessage());
            $sth->execute();
        //}

        //update part       
        /*if(isset($message->getmessageID()) && isset($message->getmessage()))
        {
            $sth = $this->db->DBH()->prepare("UPDATE    `messageen`
                                                SET     messageen.message = ? 
                                                WHERE   messageen.messageID = ?
                                                LIMIT   1");
            $sth->bindParam(1, $message->getmessage());
            $sth->bindParam(2, $message->getmessageID());
            $sth->execute();
        }*/
    }
}

index.php

<?php
//include file loader.php
include("includes/loader.php");

$guestbook = new guestbook($db);
$user = new user($db);
$messageDAO = new messageDAO($db);

//Make a array named error
$error = array();

//Get action (login/setmessage/editmessage/deletemessage)
if(isset($_GET['action']))
{   
    switch ($_GET['action'])
    {   
        //if login submit is pressed
        case 'login':
            //Check if filled
            if(isset($_POST['username']) && isset($_POST['username']))
            {
                $error['usernameEmpty'] = (bool) !strlen(trim($_POST['username']));
                $error['passwordEmpty'] = (bool) !strlen(trim($_POST['password']));
            }

            if(in_array(1, $error))
            {
                //Assign $error to smarty
                $smarty->assign('error', $error);
            }

            else
            {
                if(isset($_POST['username']) && isset($_POST['username']))
                {
                    $user->setLoggedIn(array('username'=>$_POST['username'],
                    'password'=>$_POST['password']));

                    if($user->getLoggedIn() != true)
                    {                   
                        $smarty->assign('loggedInError', $user->getLoggedIn());
                    }
                }
            }
            break;

        //Als if "place message" is pressed
        case 'placemessage':
            //if user is not logged in
            if($user->getLoggedIn() != true)
            {
                //Controleren of message-velden wel zijn ingevuld
                $error['nameEmpty'] = (bool) !strlen(trim(htmlentities(strip_tags($_POST['messagename']))));
                $error['mailEmpty'] = (bool) !strlen(trim(htmlentities(strip_tags($_POST['messageMail']))));
                $error['messageEmpty'] = (bool) !strlen(trim(htmlentities(strip_tags(str_replace('place message...','', $_POST['messageInput'])))));

                if($error['mailEmpty'] != 1)
                {
                    $error['mailInvalid'] = !filter_input((INPUT_POST), 'messageMail', FILTER_VALIDATE_EMAIL);
                }

                if(in_array(1, $error))
                {
                    $smarty->assign('error', $error);
                }

                else
                {
                    $message = new message();

                    $message->setname($_POST['messagename']);
                    $message->setMail($_POST['messageMail']);
                    $message->setmessage($_POST['messageInput']);

                    dump($message);

                    //place message             
                    $messageDAO->save($message);
                }
            }

            //if user is logged in
            else 
            {
                //is message filled?
                $error['messageEmpty'] = (bool) !strlen(trim(htmlentities(strip_tags(str_replace('place hier uw message...','', $_POST['messageInput'])))));

                if($error['messageEmpty'] != 1)
                {   
                    $user->setUser();

                    $guestbook->placemessage(array('name'=>$user->getLoggedInUsername(), 
                    'mail'=>$user->getLoggedInUserMail(),
                    'messageInput'=>$_POST['messageInput']));
                }

                else 
                {
                    $smarty->assign('error', $error);
                }
            }
            break;

        case 'deletemessage':
            $user->setUser();

            if($user->getLoggedInUserAdmin() == 1)
            {
                if(isset($_GET['messageID']) && is_numeric($_GET['messageID']) && isset($_GET['key']))
                {
                    $guestbook->setURLKey($_GET['messageID']);

                    if($guestbook->getURLKey() == $_GET['key'])
                    {                   
                        $guestbook->verwijdermessage(array('messageID'=>$_GET['messageID']));
                    }
                }
            }
            die(header("location: /index.php"));
            break;
    }
}

if(isset($_GET['pagina']) && is_numeric($_GET['pagina']))
{

    $currentpage = $_GET['pagina'];
}

else
{
    //$currentpage is 1
    $currentpage = 1;
}

$user->setUser();

//assign var to smarty
$smarty->assign('messages', $messageDAO->findmessages(array('currentpage'=>$currentpage, 'itemsPerPagina'=>10)));
$smarty->assign('user', $user);

//Pagination

$numbermessages = $messageDAO->getnumbermessages();


$totalpages = ceil($numbermessages['numbermessages'] / 10);


if($currentpage < 1)
{
    //$currentpage is 1
    $currentpage = 1;
}


if($currentpage > $totalpages)
{

    $currentpage = $totalpages;
}

$smarty->assign('numbermessages', $messageDAO->getnumbermessages());
$smarty->assign('guestbook', $guestbook);
$smarty->assign('currentpage', $currentpage);
$smarty->assign('totalpages', $totalpages);

//display index.tpl
$smarty->display('index.tpl');

Zmieniłem nazwy niektórych zmiennych i funkcji, aby miały dla ciebie sens (przetłumaczone z holenderskiego na angielski: P), więc czasami możesz znaleźć dziwne sentencje, ponieważ właśnie zrobiłem szybką zamianę itp. Baw się dobrze. Również nie jest to cały kod, ponieważ spowodowałbym, że opublikowałbym kod o wartości 20 plików: P

Bono
źródło
3

Jak wspomniano w Explosion Pills, w złożonej aplikacji większość obiektów odnosi się do komponentów aplikacji (np. Pul połączeń do bazy danych, poleceń, struktur danych, takich jak mapy skrótów), a nie do rzeczywistych obiektów (takich jak karta pokładowa, faktura lub plik mp3 ). Istnieje wiele dobrych książek na temat wzorców projektowych, które pokazują, w jaki sposób ludzie rozwiązali wiele powtarzających się problemów w tej dziedzinie. Książka GOF, jak wiadomo, jest dokładna, ale bardzo sucha, wzory Head First Design mogą być bardziej dostępne.

Pod względem analizy i projektowania w świecie rzeczywistym. Często pomocne jest myślenie w kategoriach rzeczowników i czasowników. Na przykład biblioteka wypożyczająca wideo (czy są już przestarzałe?) Może zawierać następujące rzeczy / rzeczowniki:

  • Wideo
  • Pożyczający

Pod względem czasowników:

  • Pożyczkobiorca może zabrać film na dłuższy czas
  • Pożyczkobiorca może zwrócić wideo do sklepu itp.

Można je następnie przekształcić w klasy z operacjami (już dawno nie napisałem żadnego PHP, więc go uniknę):

class Borrower
{
  public void borrow(Video video, int daysToBorrow)
  {
     ...
  }

  public void returnVideo(Video video, boolean calculateFine)
  {
     ...
  }
}

Wszystko wymaga dużo praktyki i zabawy. Najlepiej jest utknąć i uczyć się na nieudanych projektach. Moim zdaniem OO jest czymś, czego możesz się uczyć i rozwijać przez całe swoje życie (nie jest to łatwe i nie ma idealnych rozwiązań na nic). Dobry projekt jest często iteracyjny, więc spodziewaj się wypróbowania kilku różnych pomysłów na swoją aplikację internetową „Lista Craiga”.

mward
źródło
1

Najlepiej jest znaleźć sposób skoncentrowania się na rdzeniu aplikacji - „post”, „user”, „post :: FindByName ()”, „user-> Validate ()” itd. I nie martw się za dużo o hydraulice - jak przykleić posty do tabel bazy danych, jak zachować spójność wyświetlania postu między różnymi wyszukiwaniami i jak przykleić formularz „enter post” do rekordu bazy danych.

Na szczęście istnieje wiele ram, które robią to za Ciebie; dominującym paradygmatem w aplikacjach sieciowych OO jest „Model-View-Controller”, znany również jako MVC ; w PHP dostępnych jest wiele gotowych frameworków MVC.

Chociaż rozszerza to twoją potrzebę uczenia się - teraz musisz dowiedzieć się o MVC, a także OO - oznacza to, że twoje wysiłki OO są głównie ograniczone do warstwy „Modelowej”, która reprezentuje Twoją domenę biznesową; właśnie tam OO jest najbardziej naturalne i wyraziste. Większość frameworków MVC umożliwia zdefiniowanie warstwy „modelowej”, a następnie automatyczne utworzenie strony internetowej przy użyciu techniki znanej jako rusztowanie - w ten sposób uzyskuje się szybki sposób eksperymentowania z różnymi implementacjami dla modelu domeny, bez konieczności odznaczenie całej hydrauliki.

Neville Kuyt
źródło