Як можна застосувати концепції OOP для створення простого, але реального веб-додатку? [зачинено]


25

Я вже давно намагаюся обернути голову навколо ООП. Я бачу його переваги. Я прочитав багато-багато навчальних посібників і переглянув рівну кількість відео на цю тему. Я беру приклади тварин / котів / собак, я отримую приклади автомобілів / приводів. З чим я боюся, як застосувати ці поняття в реальному застосуванні. Отже, я поставив за мету створити один за допомогою OOP.

Я не прошу допомоги у синтаксисі чи написанні конкретного коду - я можу знайти це як у документації, так і за допомогою пошуку форумів і т. Д. Що мені справді потрібні певні вказівки та поштовх у правильному напрямку. Чи є якісь досвідчені програмісти, які бажають мене наставляти?

Як мій навчальний проект я хотів би створити просту рекламу "веб-додаток". Щось схоже на Craigslist, але затоплено за рівнем розмаху. Я хотів би використовувати PHP5 та MySQL, тому що я з ними знайомий.

Скажімо, є лише ці 2 випадки використання:

  1. Опублікувати щось на продаж
  2. Перегляд / пошук чогось купити

Якими «речами» мають бути об’єкти? Я можу собі уявити, що кожен предмет може бути об’єктом, але в який момент? І чому?

Так, наприклад, користувач заповнює форму "Публікація для продажу", чи повинна ця форма перетворитися на об'єкт, який передається іншому об'єкту, який вставляє значення в базу даних?

Що робити, коли інший користувач переглядає та запитує переглянути всі елементи категорії C? Чи має сенс те, що щоразу, коли додаток має підключитися до своєї бази даних, він створює об’єкт бази даних, а потім отримує купу об'єктів і відображає їх на сторінці? ... написання цього звіту, безумовно, змушує мене зрозуміти, наскільки я все ще про OOP. Будь ласка, допоможіть мені це виправити.

Якщо, на вашу думку, це не дуже хороший проект, з якого почати проникати в ООП, будь ласка, запропонуйте іншу ідею!


1
Я в тому ж човні, я думаю, що я розумію OOP - минув час, коли я спробував Java, але коли мова заходить про PHP, я б знав, як робити такі дії миттєво, як "звичайний" спосіб, але коли потрібно думати, як це буде зроблено за допомогою ООП. Я втрачаю волю до життя.
martincarlin87

Форма не перетворюється на об’єкт. Об'єкт - це примірник класу. Ви могли це бачити так. $ item-> saveItem ($ _ POST ['ім'я'], $ _POST ['опис']); редагувати Що дійсно допомогло мені зрозуміти, що OOP - це створити просту веб-програму "гостьова книга". Змушуйте користувачів входити в систему, публікувати повідомлення, редагувати повідомлення, видаляти повідомлення та шукати повідомлення тощо

@pduersteler хороша ідея, як мені це робити? Справді, це моє перше запитання щодо stackoverflow :)

@Bono, можливо, додаток для гостьових книг, як ви згадали, справді краще місце для початку. Інший, про який я думав, - це дуже простий список програм, де користувачі входять, редагують / видаляють списки, додають / редагують / видаляють елементи в цих списках. Ви б не хотіли поділитися своїм гостям програмою гостьової книги?

Я б не проти поділитися цим, хоча це було б луо кодом для публікації. Я можу поділитися простим прикладом класу з вами, якщо ви хочете. Також я не знаю, наскільки добре цей код буде працювати, тому що, відверто кажучи, минув певний час: P Я опублікую його нижче

Відповіді:


17

Я чесно вважаю, що порада тут була страшною для нових учнів, що навчаються на ЗНО. Непогано відразу починати мислити об’єкти як уявлення конкретного екземпляра "речі", визначеного деяким класом. Краще розглядати їх як складові частини машини, які мають певну взаємодію між собою, але не внутрішні внутрішні місця. Кожен з цих компонентів підтримує стан

Якщо ви хочете використовувати ORM (об'єктно-реляційне відображення) для взаємодій з БД, незалежно від того, яку структуру ви використовуєте або створюєте, напевно будуть деякі неглибокі об'єкти, що представляють таблиці, які, ймовірно, колекції "речей", але мені особисто не подобаються ORM , і я не думаю, що вони обов'язково представляють ідеальні практики OO, але вони популярні для великих веб-додатків.

Окрім цього, у вас, ймовірно, є деякі важливі компоненти, з якими потрібно запустити апарат веб-додатків, наприклад, одне або кілька підключень до БД (ви можете створити клас, який підтримує з'єднання, і ви можете запускати підготовлені запити - PDOчудово самостійно , але я б обернула це) і, можливо, шаблонна система для ваших поглядів. Можливо, ви хочете, щоб ваші контролери були і об'єктами PHP. Якщо у вас є форма для заповнення, у вас може бути об’єкт, який підтримує значення форми для P / R / G, маркер захисту CSRF і може виконувати перевірку на його входах.

Не слід намагатися шукати "речі", щоб перетворити їх на об'єкти під час створення дизайну веб-додатків та об'єктного графіка. Натомість слід подумати про логічні компоненти, які збираються разом для його створення. Я не думаю, що вам слід намагатися це змусити, і це повинно вийти досить природно, але це дуже важко зробити правильно, і вам обов'язково доведеться змінити деякі дизайнерські рішення на цьому шляху.

Моя остання порада така: склад над спадщиною - це шлях.


Основне правило, яке я маю, спеціально для динамічних мов, полягає в тому, щоб намагатися створювати класи лише в тому випадку, якщо я хочу скористатися поліморфізмом (тобто якщо ці класи будуть реалізовувати різні версії одного методу, і логіка буде залежати від цього якось). В іншому випадку я намагаюся помилитися з написанням у більш «процедурному» стилі, щоб зробити це просто.
хугомг

9

Ось як ви можете використовувати OOP для купівлі та продажу своїх домашніх тварин, та сама методика може бути використана для продажу автомобілів чи літаків; 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();
?>

28
Якщо я побачу ще один приклад з автомобілями чи тваринами, я втрачу його
Ніл МакГуган

5

На прохання ОП я поділюсь кодом своєї гостьової книги.
Клас повідомлення:

<?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;
    }
}

Клас об'єкта доступу до даних повідомлень:

<?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');

Я перейменував деякі змінні та функції, щоб мати сенс для вас (перекладений з голландської на англійську: P), щоб ви могли іноді знайти якісь дивні почуття, тому що я просто зробив швидку заміну тощо. Крім того, це не весь код, тому що це призведе до того, що я розміщую як 20 файлів, варті коду: P


3

Як зазначає «Вибухові таблетки», у складному застосуванні більшість об'єктів стосуються компонентів додатків (наприклад, пулів підключення до бази даних, команд, структур даних, таких як хешмапи), а не суб'єктів реального світу (наприклад, пропускний квиток, рахунок-фактура або mp3-файл ). Існує багато хороших книг щодо моделей дизайну, які показують вам способи вирішення багатьох проблем у цій галузі. Як відомо, книга GOF є ґрунтовною, але дуже сухою, шаблони дизайну Head First Design можуть бути більш доступними.

З точки зору аналізу та дизайну реального світу. Часто корисно думати через іменники та дієслова. Наприклад, бібліотека відеокредитування (чи є ці застарілі зараз?) Можуть мати такі речі / іменники:

  • Відео
  • Позичальник

З точки зору дієслів:

  • Позичальник може знімати відео протягом тривалого часу
  • Позичальник може повернути відео у магазин тощо.

Потім їх можна перетворити на класи з операціями (давно я вже робив будь-який PHP, тому я цього уникаю):

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

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

Це все займає багато практики та розваг. Найкраще зробити це - застрягти і навчитися невдалим дизайнам. На мою думку, ОО є чимось, що ви можете продовжувати вчитися та розвиватися протягом свого життя (це непросто і немає ідеальних рішень для чого-небудь). Хороший дизайн часто ітеративний, тому очікуйте, що ви можете спробувати кілька різних ідей для вашого веб-переліку "Список Крейга".


1

Найкраще - це знайти спосіб концентруватися на основі вашої програми - "post", "user", "post :: FindByName ()", "user-> Validate ()" тощо, і не хвилюйтеся занадто багато про сантехніка - як склеювати пости до таблиць баз даних, як підтримувати показ відображення для публікації між різними пошуковими запитами та як склеювати форму "ввести публікацію" до запису бази даних.

На щастя, існує безліч рамок, які роблять це для вас; домінуюча парадигма у веб-додатках OO - це "Model-View-Controller", також відомий як MVC ; у PHP існує декілька нестандартних каркасів MVC, які ви можете використовувати.

Хоча це розширює вашу потребу в навчанні - тепер вам доведеться дізнатися про MVC, а також про OO - це означає, що ваші зусилля OO здебільшого обмежені шаром "Модель", який представляє домен вашого бізнесу; саме там ОО є найбільш природним і виразним. Більшість фреймворків MVC дозволяють вам визначити рівень "моделі", а потім автоматично створити веб-сайт навколо цього, використовуючи техніку, відому як ліси - таким чином, ви отримаєте швидкий спосіб експериментувати з різними реалізаціями для вашої доменної моделі, без потрібно відстебнути всю сантехніку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.