단순하지만 실제적인 웹 앱을 구축하기 위해 OOP 개념을 어떻게 적용 할 수 있습니까? [닫은]


25

나는 OOP 주위에 내 머리를 감싸기 위해 오랫동안 오랫동안 노력해 왔습니다. 장점이 있습니다. 나는 많은 튜토리얼을 읽었으며 주제에 대해 동일한 양의 비디오를 보았습니다. 동물 / 고양이 / 개 예를, 자동차 / 드라이브 예를 얻습니다. 내가 고심하고있는 것은 이러한 개념을 실제 응용 프로그램에 적용하는 방법입니다. 그래서 OOP를 사용하여 빌드를 시작했습니다.

구문이나 특정 코드 작성에 대한 도움을 요청하지 않습니다. 문서 나 포럼 등을 검색하여 직접 찾을 수 있습니다. 실제로 필요한 것은 몇 가지 지침이며, 때때로 올바른 방향으로 나아가는 것입니다. 저에게 멘토링을 해줄 노련한 프로그래머가 있습니까?

내 학습 프로젝트로서 간단한 분류 된 "웹 앱"을 만들고 싶습니다. Craigslist와 비슷하지만 범위 측면에서 물이 떨어졌습니다. PHP5와 MySQL에 익숙합니다. PHP5와 MySQL을 사용하고 싶습니다.

이 두 가지 사용 사례 만 있다고 가정 해 보겠습니다.

  1. 판매 할 물건 게시
  2. 구매할 물건 탐색 / 검색

어떤 "사물"이 대상이어야합니까? 각 항목이 객체가 될 수 있다고 생각할 수 있지만 어느 시점에 있습니까? 그리고 왜?

예를 들어, 사용자는 "판매용 포스트 항목"양식을 작성합니다. 해당 양식을 데이터베이스에 값을 삽입하는 다른 오브젝트로 전달되는 오브젝트로 변환해야합니까?

다른 사용자가 탐색하고 카테고리 C의 모든 항목을 보도록 요청하는 경우는 어떻습니까? 앱이 데이터베이스에 연결해야 할 때마다 데이터베이스 개체를 만든 다음 많은 항목 개체를 가져 와서 페이지에 표시하는 것이 합리적입니까? …이 글을 쓰면 분명히 OOP에 대해 얼마나 단서가 없는지 알 수 있습니다. 그 문제를 해결하도록 도와주세요.

귀하의 의견으로는 이것이 OOP를 시작하기에 좋은 프로젝트가 아닌 경우 다른 아이디어를 제안하십시오.


1
나는 같은 보트에 있고 OOP를 이해 한다고 생각합니다 .Java를 시도한 이래 오랜 시간 이었지만 PHP에 관해서는 이와 같은 일을 즉시 '정상적인'방법으로 수행하는 방법을 알고 있지만 어떻게 생각하는지에 관해서는 알 것입니다. 그것은 OOP를 사용하여 이루어질 것입니다. 나는 살 의지를 잃었습니다.
martincarlin87

양식이 개체로 바뀌지 않았습니다. 객체는 클래스의 인스턴스입니다. 이런 식으로 볼 수 있습니다. $ item-> saveItem ($ _ POST [ '이름'], $ _POST [ '설명']); 편집 OOP를 알아내는 데 실제로 도움이 된 것은 간단한 "게스트 북"웹앱을 만드는 것입니다. 사용자 로그인, 메시지 게시, 메시지 편집, 메시지 삭제 및 메시지 검색 등

@ pduersteler 좋은 생각, 어떻게하면됩니까? 분명히, 이것은 stackoverflow에 대한 첫 번째 질문입니다 :)

@Bono 아마 당신이 언급 한 방명록 앱은 실제로 시작하기에 더 좋은 곳입니다. 내가 생각한 다른 하나는 사용자가 로그인하고 목록을 작성 / 편집 / 삭제하고 해당 목록의 항목을 추가 / 편집 / 삭제하는 매우 간단한 목록 앱이었습니다. 방명록 앱을 우리 / 나와 공유 하시겠습니까?

게시 할 코드가 많지만 공유하지 않아도됩니다. 원한다면 간단한 예제 클래스를 공유 할 수 있습니다. 또한이 코드가 얼마나 잘 작동하는지 잘 모르겠습니다. 정말로 오랜 시간이 지났기 때문에 : P 아래에 게시 할 예정입니다.

답변:


17

솔직히 여기에 조언이 새로운 OO 학습자에게는 끔찍하다고 생각합니다. 어떤 클래스에 의해 정의 된 "사물"의 특정 인스턴스를 나타내는 것으로 객체를 즉시 생각하는 것은 좋은 생각이 아닙니다. 그것들은 서로 상호 작용이 있지만 서로의 내부는 아닌 기계의 구획화 된 구성 요소로 생각하는 것이 좋습니다. 이러한 각 구성 요소는 상태를 유지합니다

DB 인터랙션에 ORM (object-relational-mapping)을 사용하려는 경우, 어떤 프레임 워크를 사용하든 생성하든 테이블을 나타내는 얕은 객체가있을 수 있습니다.이 객체는 아마도 "사물"의 모음 일 수 있지만 개인적으로 ORM을 좋아하지 않습니다 , 이것이 반드시 이상적인 OO 관행을 대표한다고 생각하지는 않지만 대형 웹 응용 프로그램에 인기가 있습니다.

게다가 하나 이상의 DB 연결과 같이 웹 응용 프로그램 시스템에서 실행해야하는 중요한 구성 요소가있을 수 있습니다 (연결을 유지하고 준비된 쿼리를 실행할 수있는 클래스를 만들 수 PDO있음). ,하지만 나는 그것을 감쌀 것입니다. 컨트롤러가 PHP 객체가되기를 원할 수도 있습니다. 채울 양식이있는 경우 CSRF 보호 토큰 인 P / R / G에 대한 양식 값을 유지하고 해당 입력에 대한 유효성 검증을 수행 할 수있는 오브젝트가있을 수 있습니다.

웹 응용 프로그램 디자인 및 개체 그래프를 구성 할 때 개체로 변환 할 "사물"을 찾지 마십시오. 대신 논리 구성 요소를 작성하기 위해 함께 제공되는 논리 구성 요소에 대해 생각해야합니다. 나는 당신이 이것을 강요하려고한다고 생각하지 않으며, 그것은 자연스럽게 이루어져야하지만 올바르게하는 것은 매우 어렵고 그 과정에서 디자인 결정을 바꿔야 할 것입니다.

마지막 조언은 이것입니다. 상속에 대한 구성은 갈 길입니다.


특히 동적 언어의 경우 경험에 따라 다형성을 활용하려는 경우에만 클래스를 작성하려고합니다 (즉, 해당 클래스가 동일한 메소드의 다른 버전을 구현하고 논리가 이에 따라 달라지는 경우). 어쩐지). 그렇지 않으면, 나는 그것을 단순하게 유지하기 위해 좀 더 "절차"스타일로 글을 쓰려고 노력한다.
hugomg

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
나는 자동차 나 동물 하나 더 OOP의 예를 볼 경우에, 나는 그것을 잃을 거입니다
닐 맥기

5

OP의 요청에 따라 방명록 코드를 공유하겠습니다.
메시지 클래스 :

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

Explosion Pills에서 언급했듯이 복잡한 응용 프로그램에서 대부분의 개체는 실제 개체 (예 : 탑승권, 송장 또는 mp3 파일)가 아닌 응용 프로그램 구성 요소 (예 : 데이터베이스 연결 풀, 명령, 해시 맵과 같은 데이터 구조)와 관련되어 있습니다. ). 사람들이이 영역에서 많은 반복되는 문제를 해결 한 방법을 보여주는 디자인 패턴에 관한 좋은 책들이 많이 있습니다. 알려진 GOF 책은 철저하지만 매우 건조합니다. Head First Design Patterns에보다 쉽게 ​​접근 할 수 있습니다.

실제 분석 및 디자인 측면에서. 명사와 동사로 생각하는 것이 종종 도움이됩니다. 예를 들어, 비디오 대출 라이브러리 (지금은 쓸모 없습니까?)에는 다음과 같은 것들 / 명사가있을 수 있습니다.

  • 비디오
  • 빌어 쓰는 사람

동사의 관점에서 :

  • 차용자는 오랫동안 비디오를 꺼낼 수 있습니다
  • 차용자는 상점 등에 비디오를 반환 할 수 있습니다.

그런 다음 이것들은 연산으로 클래스로 바뀔 수 있습니다 (PHP를 한 이후 오랜 시간이 걸리므로 피할 것입니다).

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

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

많은 연습과 놀기가 필요합니다. 가장 좋은 방법은 실패한 디자인에 갇히고 배우는 것입니다. 내 의견으로는 OO는 평생 동안 계속 배우고 발전시킬 수있는 것입니다 (쉽지 않으며 완벽한 솔루션이 없습니다). 좋은 디자인은 반복적 인 경우가 많으므로 "Craig 's List"웹앱에 대해 몇 가지 다른 아이디어를 시도해보십시오.


1

가장 좋은 방법은 "post", "user", "post :: FindByName ()", "user-> Validate ()"등 응용 프로그램의 핵심에 집중하는 방법을 찾고 걱정하지 않는 것입니다. 배관 관련 정보-데이터베이스 테이블에 게시물을 붙이는 방법, 다른 검색간에 게시물을 일관되게 표시하는 방법 및 "게시물 입력"양식을 데이터베이스 레코드에 붙이는 방법

운 좋게도이를 위해 많은 프레임 워크가 있습니다. OO 웹 애플리케이션에서 지배적 인 패러다임은 MVC 라고도 알려진 "Model-View-Controller"입니다 . PHP에는 사용할 수있는 여러 가지 상용 MVC 프레임 워크가 있습니다.

이를 통해 학습에 대한 필요성이 커지지 만 이제는 OO뿐만 아니라 MVC에 대해서도 배워야합니다. 이는 OO 노력이 대부분 비즈니스 도메인을 나타내는 "모델"계층으로 제한됨을 의미합니다. OO가 가장 자연스럽고 표현력있는 곳입니다. 대부분의 MVC 프레임 워크를 사용하면 "모델"계층을 정의한 다음 스캐 폴딩이라는 기법을 사용하여 웹 사이트를 자동으로 만들 수 있습니다. 이렇게하면 도메인 모델에 대해 다양한 구현을 실험 할 수있는 빠른 방법을 얻을 수 있습니다. 모든 배관을 뽑아야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.