Kapan menggunakan kelas statis vs instantiated

170

PHP adalah bahasa pemrograman pertama saya. Saya tidak bisa membungkus kepala saya kapan harus menggunakan kelas statis vs objek instantiated.

Saya menyadari bahwa Anda dapat menggandakan dan mengkloning objek. Namun dalam seluruh waktu saya menggunakan php, objek atau fungsi apa pun selalu berakhir sebagai nilai pengembalian (array, string, int) tunggal atau batal.

Saya memahami konsep dalam buku seperti kelas karakter video game. duplikat objek mobil dan buat yang baru merah , itu semua masuk akal, tetapi yang tidak adalah aplikasinya di php dan aplikasi web.

Contoh sederhana. Sebuah blog. Objek blog apa yang paling baik diimplementasikan sebagai objek statis atau instantiated? Kelas DB? Mengapa tidak hanya instantiate objek db dalam lingkup global? Mengapa tidak menjadikan setiap objek statis sebagai gantinya? Bagaimana dengan kinerja?

Apakah ini semua hanya gaya? Apakah ada cara yang tepat untuk melakukan hal ini?

pengguna73119
sumber

Jawaban:

123

Ini pertanyaan yang cukup menarik - dan jawabannya mungkin juga menarik ^^

Cara paling sederhana untuk mempertimbangkan berbagai hal mungkin:

  • menggunakan kelas instanciated di mana setiap objek memiliki data sendiri (seperti pengguna memiliki nama)
  • menggunakan kelas statis ketika itu hanya alat yang bekerja pada hal-hal lain (seperti, misalnya, konverter sintaks untuk kode BB ke HTML; tidak memiliki kehidupan sendiri)

(Ya, saya akui, benar-benar sangat disederhanakan ...)

Satu hal tentang metode / kelas statis adalah bahwa mereka tidak memfasilitasi pengujian unit (setidaknya dalam PHP, tetapi mungkin dalam bahasa lain juga).

Satu hal lagi tentang data statis adalah bahwa hanya ada satu contoh dari itu ada di program Anda: jika Anda mengatur MyClass :: $ myData ke beberapa nilai di suatu tempat, itu akan memiliki nilai ini, dan hanya itu, di mana saja - Berbicara tentang pengguna, Anda hanya dapat memiliki satu pengguna - yang tidak terlalu bagus, bukan?

Untuk sistem blog, apa yang bisa saya katakan? Tidak banyak yang akan saya tulis sebagai statis, sebenarnya, saya pikir; mungkin kelas DB-akses, tetapi mungkin tidak, pada akhirnya ^^

Pascal MARTIN
sumber
Jadi pada dasarnya menggunakan objek ketika bekerja dengan hal-hal unik seperti foto, pengguna, posting dll dan gunakan statis ketika itu dimaksudkan untuk hal-hal umum?
Robert Rocha
1
berbicara dengan baik tentang pengguna, Anda hanya memiliki satu pengguna aktif pada setiap permintaan. jadi haing pengguna aktif statis permintaan akan masuk akal
My1
69

Dua alasan utama untuk tidak menggunakan metode statis adalah:

  • kode menggunakan metode statis sulit untuk diuji
  • kode menggunakan metode statis sulit untuk diperluas

Memiliki panggilan metode statis di dalam beberapa metode lain sebenarnya lebih buruk daripada mengimpor variabel global. Di PHP, kelas adalah simbol global, jadi setiap kali Anda memanggil metode statis, Anda mengandalkan simbol global (nama kelas). Ini adalah kasus ketika global itu jahat. Saya punya masalah dengan pendekatan semacam ini dengan beberapa komponen Zend Framework. Ada kelas yang menggunakan pemanggilan metode statis (pabrik) untuk membangun objek. Tidak mungkin bagi saya untuk memasok pabrik lain ke instance itu untuk mendapatkan objek yang disesuaikan dikembalikan. Solusi untuk masalah ini adalah dengan hanya menggunakan instance dan metode instace dan menegakkan lajang dan sejenisnya di awal program.

Miško Hevery , yang bekerja sebagai Agile Coach di Google, memiliki teori yang menarik, atau lebih tepatnya menyarankan, bahwa kita harus memisahkan waktu pembuatan objek dari saat kita menggunakan objek. Jadi siklus hidup suatu program terbagi dua. Bagian pertama ( main()metode katakanlah), yang menangani semua objek pengkabelan dalam aplikasi Anda dan bagian yang melakukan pekerjaan yang sebenarnya.

Jadi alih-alih memiliki:

class HttpClient
{
    public function request()
    {
        return HttpResponse::build();
    }
}

Kita sebaiknya melakukan:

class HttpClient
{
    private $httpResponseFactory;

    public function __construct($httpResponseFactory)
    {
        $this->httpResponseFactory = $httpResponseFactory;
    }

    public function request()
    {
        return $this->httpResponseFactory->build();
    }
}

Dan kemudian, di halaman indeks / utama, kami akan melakukan (ini adalah langkah pengkabelan objek, atau waktu untuk membuat grafik instance yang akan digunakan oleh program):

$httpResponseFactory = new HttpResponseFactory;
$httpClient          = new HttpClient($httpResponseFactory);
$httpResponse        = $httpClient->request();

Gagasan utamanya adalah memisahkan ketergantungan dari kelas Anda. Dengan cara ini, kode ini jauh lebih mudah dikembangkan dan, bagian terpenting bagi saya, dapat diuji. Mengapa lebih penting untuk diuji? Karena saya tidak selalu menulis kode perpustakaan, jadi ekstensibilitas tidak terlalu penting, tetapi testabilitas penting ketika saya melakukan refactoring. Bagaimanapun, kode yang dapat diuji biasanya menghasilkan kode yang dapat diperluas, jadi ini bukan benar-benar situasi yang baik.

Miško Hevery juga membuat perbedaan yang jelas antara lajang dan lajang (dengan atau tanpa huruf kapital S). Perbedaannya sangat sederhana. Lajang dengan huruf "s" kecil diberlakukan oleh kabel di indeks / utama. Anda instantiate objek kelas yang tidak menerapkan pola Singleton dan berhati-hati bahwa Anda hanya meneruskan instance itu ke instance lain yang membutuhkannya. Di sisi lain, Singleton, dengan huruf kapital "S" adalah implementasi dari pola klasik (anti-). Pada dasarnya global yang menyamar yang tidak banyak digunakan di dunia PHP. Saya belum melihat satu sampai saat ini. Jika Anda ingin koneksi DB tunggal digunakan oleh semua kelas Anda lebih baik melakukannya seperti ini:

$db = new DbConnection;

$users    = new UserCollection($db);
$posts    = new PostCollection($db);
$comments = new CommentsCollection($db);

Dengan melakukan hal di atas, jelas bahwa kita memiliki seorang wanita lajang dan kita juga memiliki cara yang baik untuk menyuntikkan mock atau stub dalam tes kita. Mengejutkan bahwa unit test menghasilkan desain yang lebih baik. Tetapi masuk akal jika Anda berpikir bahwa tes memaksa Anda untuk berpikir tentang cara Anda menggunakan kode itu.

/**
 * An example of a test using PHPUnit. The point is to see how easy it is to
 * pass the UserCollection constructor an alternative implementation of
 * DbCollection.
 */
class UserCollection extends PHPUnit_Framework_TestCase
{
    public function testGetAllComments()
    {
        $mockedMethods = array('query');
        $dbMock = $this->getMock('DbConnection', $mockedMethods);
        $dbMock->expects($this->any())
               ->method('query')
               ->will($this->returnValue(array('John', 'George')));

        $userCollection = new UserCollection($dbMock);
        $allUsers       = $userCollection->getAll();

        $this->assertEquals(array('John', 'George'), $allUsers);
    }
}

Satu-satunya situasi di mana saya akan menggunakan (dan saya telah menggunakannya untuk meniru objek prototipe JavaScript di PHP 5.3) anggota statis adalah ketika saya tahu bahwa masing-masing bidang akan memiliki nilai yang sama contoh lintas. Pada titik itu Anda dapat menggunakan properti statis dan mungkin sepasang metode pengambil / penyetel statis. Bagaimanapun, jangan lupa untuk menambahkan kemungkinan untuk menimpa anggota statis dengan anggota contoh. Misalnya Zend Framework menggunakan properti statis untuk menentukan nama kelas adaptor DB yang digunakan dalam contoh Zend_Db_Table. Sudah beberapa saat sejak saya menggunakannya sehingga mungkin tidak lagi relevan, tapi itulah yang saya ingat.

Metode statis yang tidak berurusan dengan properti statis haruslah berfungsi. PHP memiliki fungsi dan kita harus menggunakannya.

Ionuț G. Stan
sumber
1
@Ionut, saya tidak ragu sedikit pun. Saya menyadari ada nilai dalam posisi / peran juga; Namun, seperti semua yang terkait XP / Agile, ia memiliki nama yang benar-benar wanita jalang.
jason
2
"Injeksi Ketergantungan" Saya percaya adalah istilah terdengar terlalu rumit untuk ini. BANYAK dan banyak membaca tentang itu di seluruh SO dan google-land. Sejauh "lajang" pergi, berikut adalah sesuatu yang benar-benar membuat saya berpikir: slideshare.net/go_oh/… Pembicaraan tentang mengapa lajang PHP benar-benar tidak masuk akal. Semoga ini berkontribusi sedikit untuk pertanyaan lama :)
dudewad
22

Jadi dalam PHP statis bisa diterapkan ke fungsi atau variabel. Variabel non-statis terkait dengan instance kelas tertentu. Metode non-statis bertindak atas instance kelas. Jadi mari kita membuat kelas yang disebut BlogPost.

titleakan menjadi anggota yang tidak statis. Ini berisi judul posting blog itu. Kami mungkin juga memiliki metode yang disebut find_related(). Itu tidak statis karena memerlukan informasi dari instance tertentu dari kelas posting blog.

Kelas ini akan terlihat seperti ini:

class blog_post {
    public $title;
    public $my_dao;

    public function find_related() {
        $this->my_dao->find_all_with_title_words($this->title);
    }
}

Di sisi lain, menggunakan fungsi statis, Anda dapat menulis kelas seperti ini:

class blog_post_helper {
    public static function find_related($blog_post) {
         // Do stuff.
    }
}

Dalam hal ini, karena fungsinya statis dan tidak bekerja pada posting blog tertentu, Anda harus mengirimkan posting blog sebagai argumen.

Pada dasarnya ini adalah pertanyaan tentang desain berorientasi objek. Kelas-kelas Anda adalah kata benda dalam sistem Anda, dan fungsi-fungsi yang bekerja pada mereka adalah kata kerja. Fungsi statis bersifat prosedural. Anda meneruskan objek fungsi sebagai argumen.


Pembaruan: Saya juga menambahkan bahwa keputusan jarang antara metode instance dan metode statis, dan lebih banyak lagi antara menggunakan kelas dan menggunakan array asosiatif. Misalnya, dalam aplikasi blogging, Anda membaca posting blog dari database dan mengubahnya menjadi objek, atau membiarkannya dalam kumpulan hasil dan memperlakukannya sebagai array asosiatif. Kemudian Anda menulis fungsi yang menggunakan array asosiatif atau daftar array asosiatif sebagai argumen.

Dalam skenario OO, Anda menulis metode di BlogPostkelas Anda yang bertindak pada posting individu, dan Anda menulis metode statis yang bertindak pada kumpulan posting.

Rafe
sumber
4
Jawaban ini cukup bagus, khususnya dengan pembaruan yang Anda lakukan, karena itulah alasan saya akhirnya pada pertanyaan ini. Saya mengerti fungsionalitas "::" vs "$ this", tetapi ketika Anda menambahkan ke dalam akun, contoh yang Anda berikan tentang blogpost diekstraksi dari DB dalam array asosiatif .. Ia menambahkan dimensi baru, yaitu juga dalam nada yang mendasari pertanyaan ini.
Gerben Jacobs
Ini adalah salah satu jawaban praktis (versus teoretis) terbaik untuk pertanyaan PHP yang banyak ditanyakan ini, tambahannya sangat membantu. Saya pikir ini adalah jawaban yang dicari oleh banyak programmer PHP, terangkat!
ajmedway
14

Apakah ini semua hanya gaya?

Jauh, ya. Anda dapat menulis program berorientasi objek yang sangat baik tanpa menggunakan anggota statis. Bahkan beberapa orang akan berpendapat bahwa anggota statis adalah pengotor di tempat pertama. Saya akan menyarankan bahwa - sebagai pemula di oop - Anda mencoba untuk menghindari anggota statis bersama-sama. Ini akan memaksa Anda ke arah penulisan dalam gaya berorientasi objek daripada gaya prosedural .

troelskn
sumber
13

Saya memiliki pendekatan berbeda untuk sebagian besar jawaban di sini, terutama ketika menggunakan PHP. Saya pikir semua kelas harus statis kecuali Anda punya alasan bagus mengapa tidak. Beberapa alasan "mengapa tidak" adalah:

  • Anda membutuhkan beberapa instance kelas
  • Kelas Anda perlu diperpanjang
  • Bagian dari kode Anda tidak dapat membagikan variabel kelas dengan bagian lain

Biarkan saya ambil satu contoh. Karena setiap skrip PHP menghasilkan kode HTML, kerangka kerja saya memiliki kelas penulis html. Ini memastikan bahwa tidak ada kelas lain yang akan mencoba menulis HTML karena itu adalah tugas khusus yang harus dikonsentrasikan ke dalam satu kelas.

Biasanya, Anda akan menggunakan kelas html seperti ini:

html::set_attribute('class','myclass');
html::tag('div');
$str=html::get_buffer();

Setiap kali get_buffer () dipanggil, ia me-reset semuanya sehingga kelas berikutnya untuk menggunakan penulis html dimulai dalam keadaan yang dikenal.

Semua kelas statis saya memiliki fungsi init () yang perlu dipanggil sebelum kelas digunakan untuk pertama kalinya. Ini lebih dari konvensi daripada keharusan.

Alternatif untuk kelas statis dalam hal ini berantakan. Anda tidak ingin setiap kelas yang perlu menulis sedikit html harus mengelola instance dari penulis html.

Sekarang saya akan memberi Anda contoh kapan tidak menggunakan kelas statis. Kelas formulir saya mengelola daftar elemen formulir seperti input teks, daftar dropdown, dan lainnya. Biasanya digunakan seperti ini:

$form = new form(stuff here);
$form->add(new text(stuff here));
$form->add(new submit(stuff here));
$form->render(); // Creates the entire form using the html class

Tidak ada cara Anda bisa melakukan ini dengan kelas statis, terutama mengingat bahwa beberapa konstruktor dari setiap kelas tambahan melakukan banyak pekerjaan. Juga, rantai pewarisan untuk semua elemen cukup kompleks. Jadi ini adalah contoh yang jelas di mana kelas statis tidak boleh digunakan.

Sebagian besar kelas utilitas seperti yang mengonversi / memformat string adalah kandidat yang baik untuk menjadi kelas statis. Aturan saya sederhana: semuanya berjalan statis di PHP kecuali ada satu alasan mengapa tidak.

JG Estiot
sumber
dapatkah Anda menulis contoh poin di bawah ini "Bagian dari kode Anda tidak dapat berbagi variabel kelas dengan bagian lain" #JG Estiot
khurshed alam
10

"Memiliki panggilan metode statis di dalam beberapa metode lain sebenarnya lebih buruk daripada mengimpor variabel global." (tentukan "lebih buruk") ... dan "Metode statis yang tidak berurusan dengan properti statis haruslah fungsi".

Keduanya adalah pernyataan yang cukup menyapu. Jika saya memiliki seperangkat fungsi yang terkait dalam materi pelajaran, tetapi data contoh benar-benar tidak sesuai, saya lebih suka mereka didefinisikan dalam kelas dan tidak masing-masing di ruang nama global. Saya hanya menggunakan mekanisme yang tersedia di PHP5 untuk

  • beri mereka semua namespace - menghindari bentrokan nama apa pun
  • menjaga mereka secara fisik ditempatkan bersama daripada menjadi tersebar di seluruh proyek - pengembang lain dapat lebih mudah menemukan apa yang sudah tersedia dan kecil kemungkinannya untuk menemukan kembali roda
  • izinkan saya menggunakan konstanta kelas alih-alih definisi global untuk nilai sihir apa pun

itu hanya cara yang nyaman untuk menegakkan kohesi yang lebih tinggi dan kopling yang lebih rendah.

Dan FWIW - tidak ada hal seperti itu, setidaknya di PHP5, sebagai "kelas statis"; metode dan properti bisa statis. Untuk mencegah instantiasi kelas, seseorang dapat mendeklarasikannya secara abstrak.

Grantwparks
sumber
2
"Untuk mencegah instantiasi kelas, seseorang dapat menyatakannya abstrak, juga" - Saya sangat suka itu. Saya sebelumnya membaca tentang orang yang membuat __construct () fungsi pribadi, tapi saya pikir jika saya perlu, saya mungkin akan menggunakan abstrak
Kavi Siegel
7

Pertama-tama tanyakan pada diri Anda, benda apa yang akan diwakili? Sebuah instance objek baik untuk beroperasi pada set data dinamis yang terpisah.

Contoh yang baik adalah ORM atau lapisan abstraksi basis data. Anda mungkin memiliki beberapa koneksi basis data.

$db1 = new Db(array('host' => $host1, 'username' => $username1, 'password' => $password1));
$db2 = new Db(array('host' => $host2, 'username' => $username2, 'password' => $password2));

Kedua koneksi sekarang dapat beroperasi secara independen:

$someRecordsFromDb1 = $db1->getRows($selectStatement);
$someRecordsFromDb2 = $db2->getRows($selectStatement);

Sekarang di dalam paket / pustaka ini, mungkin ada kelas lain seperti Db_Row, dll. Untuk mewakili baris tertentu yang dikembalikan dari pernyataan SELECT. Jika kelas Db_Row ini adalah kelas statis, maka itu akan mengasumsikan Anda hanya memiliki satu baris data dalam satu database dan tidak mungkin untuk melakukan apa yang bisa instance objek. Dengan sebuah instance, Anda sekarang dapat memiliki jumlah baris yang tidak terbatas dalam jumlah tabel yang tidak terbatas dalam jumlah database yang tidak terbatas. Satu-satunya batasan adalah perangkat keras server;).

Misalnya, jika metode getRows pada objek Db mengembalikan array objek Db_Row, Anda sekarang dapat beroperasi di setiap baris secara independen satu sama lain:

foreach ($someRecordsFromDb1 as $row) {
    // change some values
    $row->someFieldValue = 'I am the value for someFieldValue';
    $row->anotherDbField = 1;

    // now save that record/row
    $row->save();
}

foreach ($someRecordsFromDb2 as $row) {
    // delete a row
    $row->delete();
}

Contoh yang baik dari kelas statis adalah sesuatu yang menangani variabel registri, atau variabel sesi karena hanya akan ada satu registri atau satu sesi per pengguna.

Di salah satu bagian aplikasi Anda:

Session::set('someVar', 'toThisValue');

Dan di bagian lain:

Session::get('someVar'); // returns 'toThisValue'

Karena hanya akan ada satu pengguna pada satu waktu per sesi, tidak ada gunanya membuat contoh untuk Sesi.

Saya harap ini membantu, bersama dengan jawaban lain untuk membantu menjernihkan semuanya. Sebagai catatan tambahan, lihat " kohesi " dan " kopling ". Mereka menguraikan beberapa praktik yang sangat, sangat baik untuk digunakan saat menulis kode Anda yang berlaku untuk semua bahasa pemrograman.

Tres
sumber
6

Jika kelas Anda statis itu berarti Anda tidak bisa meneruskan objeknya ke kelas lain (karena tidak ada contoh yang memungkinkan) sehingga berarti semua kelas Anda akan langsung menggunakan kelas statis itu yang berarti kode Anda sekarang dipasangkan dengan kelas. .

Kopling ketat membuat kode Anda kurang dapat digunakan kembali, rapuh dan rentan terhadap bug. Anda ingin menghindari kelas statis agar dapat melewatkan instance kelas ke kelas lain.

Dan ya ini hanya satu dari banyak alasan lain yang beberapa di antaranya telah disebutkan.

Muhammad Hasan Khan
sumber
3

Secara umum, Anda harus menggunakan variabel anggota dan fungsi anggota kecuali itu benar-benar harus dibagi antara semua contoh atau kecuali Anda membuat singleton. Menggunakan data anggota dan fungsi anggota memungkinkan Anda menggunakan kembali fungsi Anda untuk beberapa bagian data yang berbeda, sedangkan Anda hanya dapat memiliki satu salinan data tempat Anda beroperasi jika Anda menggunakan data dan fungsi statis. Selain itu, meskipun tidak berlaku untuk PHP, fungsi statis dan data menyebabkan kode menjadi non-reentrant, sedangkan data kelas memfasilitasi reentrancy.

Michael Aaron Safyan
sumber
3

Saya ingin mengatakan bahwa pasti ada kasus di mana saya ingin variabel statis - dalam aplikasi lintas bahasa. Anda dapat memiliki kelas tempat Anda menggunakan bahasa (mis. $ _SESSION ['language']) dan pada gilirannya mengakses kelas lain yang dirancang seperti ini:

Srings.php //The main class to access
StringsENUS.php  //English/US 
StringsESAR.php  //Spanish/Argentina
//...etc

Menggunakan Strings :: getString ("somestring") adalah cara yang bagus untuk abstrak penggunaan bahasa Anda dari aplikasi Anda. Anda bisa melakukannya sesuka Anda tetapi dalam kasus ini setiap file string memiliki konstanta dengan nilai string yang diakses oleh kelas Strings berfungsi dengan cukup baik.

dudewad
sumber