OOPの概念を適用して、シンプルだが実際のWebアプリを構築するにはどうすればよいですか?[閉まっている]


25

私は頭をOOPに巻き付けるために長い間努力してきました。その利点がわかります。私は多くのチュートリアルを読み、このテーマについて同量のビデオを見ました。動物/猫/犬の例、車/ドライブの例があります。私が苦労しているのは、これらの概念を実際のアプリケーションに適用する方法です。そこで、OOPを使用してビルドすることにしました。

私は構文の助けを求めたり、特定のコードを書いたりするのではなく、ドキュメントやフォーラムなどを検索することで、自分自身を見つけることができます。私を指導してくれるベテランのプログラマーはいますか?

私の学習プロジェクトとして、シンプルなクラシファイド「ウェブアプリ」を構築したいと思います。Craigslistに似たものですが、範囲の点でかなり落ち着きました。PHP5とMySQLは使い慣れているので、使用したいと思います。

これらの2つのユースケースのみがあるとしましょう:

  1. 売り物を投稿する
  2. 購入するものを閲覧/検索する

オブジェクトとなる「もの」は何ですか?各アイテムがオブジェクトになる可能性があると想像できますが、どの時点でですか?なぜ?

たとえば、ユーザーが「販売アイテムを投稿する」フォームに入力すると、そのフォームはオブジェクトに変換され、値をデータベースに挿入する別のオブジェクトに渡されますか?

別のユーザーがカテゴリCのすべてのアイテムを閲覧してリクエストする場合はどうでしょうか?アプリがデータベースに接続する必要があるときはいつでも、データベースオブジェクトを作成し、アイテムオブジェクトの束を取得してページに表示するのは理にかなっていますか?…これを書くことで、OOPについてまだ無知であることを実感できます。それを修正するのを手伝ってください。

あなたの意見で、これがOOPに足を踏み入れるのに適したプロジェクトではない場合、別のアイデアを提案してください。


1
私は同じ船に乗っています、私 OOPを理解していると思います -Javaを試みてからしばらく経ちましたが、PHPに関しては、このようなことを即座に「通常の」方法で行う方法を知っていますが、それは私が生きる意志を失うOOPを使用して行われます。
martincarlin87

フォームはオブジェクトに変換されません。オブジェクトはクラスのインスタンスです。このように見えました。$ item-> saveItem($ _ POST ['name']、$ _POST ['description']); 編集 OOPの理解に本当に役立ったのは、単純な「ゲストブック」ウェブアプリを作成することです。ユーザーにログイン、メッセージの投稿、メッセージの編集、メッセージの削除、メッセージの検索などを行わせる

@pduersteler良いアイデア、それをどうやってやるの?確かに、これはstackoverflowに関する私の最初の質問です:)

@Bonoおそらく、あなたが言ったようなゲストブックアプリは、開始するのにより良い場所です。私が考えていたもう1つは、ユーザーがログイン、リストの作成/編集/削除、リスト内のアイテムの追加/編集/削除を行う非常にシンプルなリストアプリです。ゲストブックアプリを私たちと共有してもよろしいですか?

共有するのは気にしませんが、投稿するコードは大したものになります。必要に応じて、簡単なサンプルクラスを共有できます。率直に言って、それがしばらくしているので、また、私は、このコードが動作するかよく知っていない:P私は以下のことを投稿します

回答:


17

ここでのアドバイスは、これまで新しいオブジェクト指向学習者にとってひどいものだったと思います。オブジェクトを、あるクラスによって定義された「もの」の特定のインスタンスの表現としてすぐに考え始めるのは得策ではありません。相互に何らかの相互作用はあるが、相互の内部構造はない、マシンのコンパートメント化されたコンポーネントと考える方が適切です。これらの各コンポーネントは状態を維持します

DBインタラクションにORM(オブジェクトリレーショナルマッピング)を使用する場合、使用または作成するフレームワークには、テーブルを表す浅いオブジェクトが含まれている可能性があります。 、そしてそれらは必ずしも理想的なオブジェクト指向のプラクティスを表しているとは思いませんが、大規模なWebアプリで人気があります。

それに加えて、おそらく1つ以上のDB接続など、Webアプリマシンが実行する必要があるいくつかの重要なコンポーネントがあります(接続を維持し、準備されたクエリを実行できるクラスを作成できます- PDOそれ自体で素晴らしいです)、しかし、私はそれをラップします)、そしておそらくあなたのビューのためのテンプレートシステム。コントローラーをPHPオブジェクトにすることもできます。記入するフォームがある場合、CSRF保護トークンであるP / R / Gのフォーム値を保持し、その入力で検証を実行できるオブジェクトがあります。

Webアプリのデザインとオブジェクトグラフを作成するときに、オブジェクトに変わる「もの」を探してはいけません。代わりに、一緒になって作成する論理コンポーネントについて考える必要があります。これを強制しようとする必要はないと思いますし、かなり自然に来るはずですが、正しく行うのは非常に難しく、途中でいくつかの設計決定を変更する必要があります。

私の最後のアドバイスはこれです。継承よりも合成が進むべき方法です。


私が持っている経験則は、特に動的言語の場合、ポリモーフィズムを利用したい場合にのみクラスを作成しようとすることです(つまり、それらのクラスが同じメソッドの異なるバージョンを実装し、ロジックがそれに依存する場合)どういうわけか)。それ以外の場合は、シンプルにするために、より「手続き型」のスタイルで書くことに失敗します。
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
車や動物でもう1つの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ファイル分のコードを投稿することになってしまうためです。


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)
  {
     ...
  }
}

すべての練習と遊びが必要です。最善の方法は、行き詰まって、失敗したデザインから学ぶことです。私の意見では、オブジェクト指向は、あなたが生涯にわたって学び、開発し続けることができるものです(簡単ではなく、何に対しても完璧な解決策はありません)。良いデザインは多くの場合反復的であるため、「Craig's List」webappのいくつかの異なるアイデアを試してみてください。


1

最善の方法は、アプリケーションのコアに集中する方法を見つけることです-「post」、「user」、「post :: FindByName()」、「user-> Validate()」など。配管についてあまりにも多く-投稿をデータベーステーブルに接着する方法、異なる検索間で投稿の表示を一貫させる方法、「投稿を入力する」フォームをデータベースレコードに接着する方法。

幸いなことに、あなたのためにこれを行う多くのフレームワークがあります。OO Webアプリケーションの主要なパラダイムは、MVCとしても知られる「Model-View-Controller」です。PHPには、多数の既製のMVCフレームワークを使用できます。

これにより、学習の必要性が広がりますが、MVCとOOについて学習する必要がありますが、これは、OOの取り組みのほとんどがビジネスドメインを表す「モデル」層に制限されることを意味します。それがオブジェクト指向が最も自然で表現力豊かなところです。ほとんどのMVCフレームワークでは、「モデル」レイヤーを定義してから、scaffoldingと呼ばれる手法を使用してその周辺にWebサイトを自動的に作成できます。この方法により、ドメインモデルのさまざまな実装を簡単に試すことができます。すべての配管を外す必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.