Statik ve somutlaştırılmış sınıflar ne zaman kullanılır?


170

PHP benim ilk programlama dilim. Statik sınıflara karşı somut nesnelerin ne zaman kullanılacağını kafamın etrafına dolamıyorum.

Nesneleri çoğaltabileceğiniz ve klonlayabileceğinizin farkındayım. Ancak tüm zaman php kullanarak herhangi bir nesne veya işlev her zaman tek bir dönüş (dizi, dize, int) değeri veya geçersiz olarak sona erdi.

Video oyunu karakter sınıfı gibi kitaplardaki kavramları anlıyorum. araba nesnesini çoğaltın ve yenisini kırmızı yapın , hepsi mantıklıdır, ancak php ve web uygulamalarındaki uygulaması nedir.

Basit bir örnek. Bir blog. Bir blogun hangi nesneleri en iyi statik veya somut nesneler olarak uygulanır? DB sınıfı? Neden sadece db nesnesini global kapsamda başlatmıyorsunuz? Neden her nesneyi statik yapmıyorsunuz? Performans ne olacak?

Hepsi sadece stil mi? Bunu yapmanın uygun bir yolu var mı?

Yanıtlar:


123

Bu oldukça ilginç bir soru - ve cevaplar da ilginç olabilir ^ ^

Bir şeyi düşünmenin en basit yolu şunlar olabilir:

  • her nesnenin kendi başına verileri olduğu örneklenmiş bir sınıf kullanın (kullanıcının adı olduğu gibi)
  • sadece diğer şeyler üzerinde çalışan bir araç olduğunda statik bir sınıf kullanın (örneğin, BB kodunu HTML için bir sözdizimi dönüştürücü; tek başına bir hayatı yoktur)

(Evet, itiraf ediyorum, gerçekten aşırı basitleştirilmiş ...)

Statik yöntemler / sınıflarla ilgili bir şey, birim testini kolaylaştırmamalarıdır (en azından PHP'de, ancak muhtemelen diğer dillerde de).

Statik verilerle ilgili başka bir şey, programınızda yalnızca bir örneğinin bulunmasıdır: MyClass :: $ myData'yı bir yerde bir değere ayarlarsanız, bu değere sahip olur ve yalnızca her yerde - Kullanıcı hakkında konuşma, sadece bir kullanıcıya sahip olabilirsiniz - ki bu o kadar da iyi değil, değil mi?

Bir blog sistemi için ne söyleyebilirim? Statik olarak yazacağım pek bir şey yok, aslında bence; sonunda DB-erişim sınıfı, ama muhtemelen değil ^^


Temel olarak fotoğraf, kullanıcı, posta vb. Gibi benzersiz şeylerle çalışırken nesneleri kullanın ve genel şeyler için kullanıldığında statik kullanın?
Robert Rocha

1
kullanıcı hakkında konuşursak, her istekte yalnızca bir aktif kullanıcınız vardır. statik istek etkin kullanıcı haing mantıklı olurdu
My1

69

Statik yöntemleri kullanmaya karşı başlıca iki neden:

  • statik yöntemler kullanarak kod test etmek zordur
  • statik yöntemler kullanarak kodun genişletilmesi zordur

Statik bir yöntem çağrısının başka bir yöntemin içinde olması, genel bir değişkeni içe aktarmaktan daha kötüdür. PHP'de sınıflar genel sembollerdir, dolayısıyla statik bir yöntemi her çağırdığınızda genel bir sembole (sınıf adı) güvenirsiniz. Küresel kötülüğün olduğu bir durum bu. Zend Framework'ün bazı bileşenlerinde bu tür bir yaklaşımla sorunlar yaşadım. Nesneleri oluşturmak için statik yöntem çağrıları (fabrikalar) kullanan sınıflar vardır. Özelleştirilmiş bir nesnenin geri döndürülmesi için bu örneğe başka bir fabrika tedarik etmem imkansızdı. Bu sorunun çözümü, programın başında yalnızca örnek ve instagram yöntemlerini kullanmak ve tekilleri ve benzerlerini uygulamaktır.

Google'da Çevik Koç olarak çalışan Miško Hevery , ilginç bir teoriye sahiptir veya daha ziyade, nesne oluşturma zamanını nesneyi kullandığımız zamandan ayırmamız gerektiğini tavsiye eder. Yani bir programın yaşam döngüsü ikiye ayrılır. Uygulamanızdaki main()tüm nesne kablolarını ve gerçek işi yapan kısmı ele alan ilk bölüm ( yöntem diyelim).

Yani sahip olmak yerine:

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

Yapmayı tercih etmeliyiz:

class HttpClient
{
    private $httpResponseFactory;

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

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

Ve sonra, indeks / ana sayfada yapardık (bu, nesne kablolama adımı veya program tarafından kullanılacak örneklerin grafiğini oluşturma zamanıdır):

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

Ana fikir bağımlılıkları sınıflarınızdan ayırmaktır. Bu şekilde kod çok daha genişletilebilir ve benim için en önemli kısım test edilebilir. Test edilebilir olmak neden daha önemlidir? Her zaman kütüphane kodu yazmadım, bu nedenle genişletilebilirlik o kadar önemli değil, ancak yeniden düzenleme yaparken test edilebilirlik önemlidir. Her neyse, test edilebilir kod genellikle genişletilebilir kod verir, bu yüzden gerçekten bir ya da durum değildir.

Miško Hevery ayrıca singletons ve Singletons (büyük S ile veya büyük S olmadan) arasında net bir ayrım yapar. Aradaki fark çok basit. Küçük harfleri "s" olan tekil parametreler dizin / ana kablo tesisatı tarafından zorlanır. Sen bir sınıfın bir nesne örneğini değil Singleton deseni uygulamak ve sadece ihtiyacı başka örneğine bu örneği geçmesi dikkat ediniz. Öte yandan, Singleton, sermayesi "S" olan klasik (anti-) örüntünün bir uygulamasıdır. Temelde PHP dünyasında fazla bir kullanımı olmayan kılık değiştirmiş bir küresel. Bu noktaya kadar bir tane görmedim. Tek bir DB bağlantısının tüm sınıflarınız tarafından kullanılmasını istiyorsanız bunu şu şekilde yapmak daha iyidir:

$db = new DbConnection;

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

Yukarıdakileri yaparak bir singletonumuz olduğu ve testlerimize bir sahte ya da saplama enjekte etmenin güzel bir yolunun olduğu açıktır. Ünite testlerinin nasıl daha iyi bir tasarıma yol açtığı şaşırtıcıdır. Ancak, testlerin sizi bu kodu kullanma şeklinizi düşünmeye zorladığını düşündüğünüzde çok mantıklıdır.

/**
 * 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);
    }
}

Kullanabileceğim tek durum (ve PHP 5.3'teki JavaScript prototip nesnesini taklit etmek için kullandım) statik üyeleri, ilgili alanın aynı değere sahip çapraz örneğe sahip olacağını bildiğim zamandır. Bu noktada bir statik özellik ve belki bir çift statik alıcı / ayarlayıcı yöntemi kullanabilirsiniz. Her neyse, statik üyeyi bir örnek üyeyle geçersiz kılma olasılığını eklemeyi unutmayın. Örneğin Zend Framework, örneklerinde kullanılan DB bağdaştırıcı sınıfının adını belirtmek için statik bir özellik kullanıyordu Zend_Db_Table. Onları kullandığımdan beri bir süredir bu yüzden artık alakalı olmayabilir, ancak bu şekilde hatırlıyorum.

Statik özelliklerle ilgilenmeyen statik yöntemler işlevler olmalıdır. PHP'nin işlevleri vardır ve bunları kullanmalıyız.


1
@Ionut, bundan biraz şüphem yok. Pozisyonda / rolde de bir değer olduğunun farkındayım; ancak, XP / Agile ile ilgili her şey gibi, gerçekten hoppa bir isme sahiptir.
jason

2
"Bağımlılık Enjeksiyonu" Bunun çok karmaşık sondaj terimi olduğuna inanıyorum. Çok SO ve google-land üzerinde okuma bir sürü. Bildiğim kadarıyla "singleton" gitmek, gerçekten beni düşündüren bir şey: slideshare.net/go_oh/… PHP singletonların neden gerçekten mantıklı olmadığı üzerine bir konuşma. Umarım bu eski bir soruya biraz katkıda bulunur :)
dudewad

22

Yani PHP'de statik fonksiyonlara veya değişkenlere uygulanabilir. Statik olmayan değişkenler bir sınıfın belirli bir örneğine bağlıdır. Statik olmayan yöntemler bir sınıf örneğine etki eder. Öyleyse bir sınıf oluşturalım BlogPost.

titlestatik olmayan bir üye olur. Bu blog gönderisinin başlığını içerir. Ayrıca bir yöntemimiz de olabilir find_related(). Blog yazı sınıfının belirli bir örneğinden bilgi gerektirdiğinden statik değildir.

Bu sınıf şöyle görünecektir:

class blog_post {
    public $title;
    public $my_dao;

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

Öte yandan, statik işlevleri kullanarak, aşağıdaki gibi bir sınıf yazabilirsiniz:

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

Bu durumda, işlev statik olduğundan ve herhangi bir blog yayınına etki etmediğinden, blog yayınını bağımsız değişken olarak iletmeniz gerekir.

Temel olarak bu nesne yönelimli tasarım hakkında bir sorudur. Sınıflarınız sisteminizdeki isimlerdir ve bunlara etki eden işlevler fiillerdir. Statik fonksiyonlar prosedüreldir. Fonksiyonların nesnesini argümanlar olarak iletirsiniz.


Güncelleme: Ayrıca, kararın nadiren örnek yöntemleri ile statik yöntemler arasında ve daha çok sınıfları kullanma ile ilişkilendirilebilir dizileri kullanma arasında olduğunu eklerim. Örneğin, bir blog uygulamasında, blog yayınlarını veritabanından okuyup nesnelere dönüştürür veya sonuç kümesinde bırakır ve ilişkilendirilebilir diziler olarak görürsünüz. Daha sonra ilişkilendirilebilir dizileri veya ilişkilendirilebilir dizilerin listelerini bağımsız değişken olarak alan işlevler yazarsınız.

OO senaryosunda, BlogPostsınıfınıza tek tek yayınlar üzerinde etkili yöntemler yazarsınız ve yazı koleksiyonları üzerinde etkili olan statik yöntemler yazarsınız.


4
Bu cevap, özellikle yaptığınız güncellemeyle oldukça iyi, çünkü bu soruyu sonuçlandırmamın nedeni bu. "::" vs "$ this" işlevselliğini anlıyorum, ancak hesaba eklediğinizde, ilişkilendirilmiş bir dizideki bir DB'den ayıklanan blogpostlarına verdiğiniz örnek yepyeni bir boyut ekliyor. ayrıca bu sorunun altında yatan tonda.
Gerben Jacobs

Bu çok sorulan PHP sorusuna en pratik (teorik olarak) cevaplardan biridir, zeyilname çok yararlıdır. Bence bu, PHP programcılarının birçoğunun arayıp bulduklarının cevabı!
ajmedway

14

Hepsi sadece stil mi?

Uzun bir yol, evet. Statik üyeler kullanmadan mükemmel nesne yönelimli programlar yazabilirsiniz. Aslında bazı insanlar ilk etapta statik üyelerin bir safsızlık olduğunu iddia ederler. Oop'ta yeni başlayan biri olarak, hep birlikte statik üyelerden kaçınmaya çalışmanızı öneririm. Sizi prosedür tarzından ziyade nesne yönelimli bir nesne yazma yönünde zorlar .


13

Burada, özellikle PHP kullanırken, çoğu cevap için farklı bir yaklaşım var. Neden olmasın iyi bir nedeniniz yoksa tüm sınıfların statik olması gerektiğini düşünüyorum. "Neden olmasın" nedenlerinden bazıları:

  • Sınıfın birden çok örneğine ihtiyacınız var
  • Sınıfınızın genişletilmesi gerekiyor
  • Kodunuzun bazı bölümleri, sınıf değişkenlerini başka hiçbir bölümle paylaşamaz

Bir örnek vereyim. Her PHP betiği HTML kodu ürettiğinden, çerçevemde bir html yazar sınıfı vardır. Bu, başka hiçbir sınıfın tek bir sınıfa konsantre olması gereken özel bir görev olduğu için HTML yazmaya çalışmaz.

Genellikle, html sınıfını şu şekilde kullanırsınız:

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

Get_buffer () her çağrıldığında, html yazıcısını kullanacak bir sonraki sınıfın bilinen bir durumda başlaması için her şeyi sıfırlar.

Tüm statik sınıflarım, sınıf ilk kez kullanılmadan önce çağrılması gereken bir init () işlevine sahiptir. Bu, bir zorunluluktan ziyade konvansiyonladır.

Bu durumda statik bir sınıfa alternatif dağınıktır. Küçük bir html yazması gereken her sınıfın, html yazarının bir örneğini yönetmesini istemezsiniz.

Şimdi size statik sınıfların ne zaman kullanılmayacağına dair bir örnek vereceğim. Form sınıfım, metin girişleri, açılır listeler ve daha fazlası gibi form öğelerinin listesini yönetir. Genellikle şu şekilde kullanılır:

$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

Statik sınıflarla bunu yapabilmenin hiçbir yolu yoktur, özellikle de eklenen her sınıfın bazı kurucularının çok fazla iş yaptığını düşünüyoruz. Ayrıca, tüm elementler için kalıtım zinciri oldukça karmaşıktır. Dolayısıyla bu, statik sınıfların kullanılmaması gereken açık bir örnektir.

Dizeleri dönüştüren / biçimlendirenler gibi çoğu yardımcı program sınıfı, statik bir sınıf olmak için iyi adaylardır. Kuralım basit: neden olmamalı bir nedeni olmadığı sürece her şey PHP statik olur.


Eğer #JG Estiot "başka parçalarla sınıf değişkenleri paylaşamaz kodunuzun Parçaları" noktasının altındaki bir örnek yazabiliriz
Khurshed alam

10

"Başka bir yöntemin içinde statik yöntem çağrısı olması, genel bir değişkeni içe aktarmaktan daha kötüdür." ("daha kötü" tanımlayın) ... ve "Statik özelliklerle ilgilenmeyen statik yöntemler işlevler olmalıdır".

Bunların ikisi de oldukça kapsamlı ifadelerdir. Konu ile ilgili bir dizi işlevim var, ancak örnek verileri tamamen uygunsuzsa, bunların her birini genel ad alanında değil, bir sınıfta tanımlamasını tercih ederim. Ben sadece PHP5 mevcut mekanik kullanmak için

  • hepsine bir ad alanı verin - herhangi bir isim çakışmasından kaçının
  • bir projeye dağılmak yerine onları fiziksel olarak bir arada tutun - diğer geliştiriciler zaten mevcut olanı daha kolay bulabilir ve tekerleği yeniden icat etme olasılığı daha düşüktür
  • sihirli değerler için global tanımlamalar yerine sınıf sabitlerini kullanmama izin verin

daha yüksek uyum ve daha düşük kuplajı uygulamak için tamamen uygun bir yoldur.

Ve FWIW - en azından PHP5'te "statik sınıflar" diye bir şey yoktur; yöntemler ve özellikler statik olabilir. Sınıfın örneklenmesini önlemek için kişi soyut olarak da beyan edebilir.


2
"Sınıfın örneklenmesini önlemek için, bir de soyut ilan edebilir" - Bunu çok seviyorum. Daha önce __construct () 'ı özel bir işlev yapan insanlar hakkında okudum, ama sanırım ihtiyacım olursa soyut kullanacağım
Kavi Siegel

7

Önce kendinize sorun, bu nesne neyi temsil edecek? Bir nesne örneği, ayrı dinamik veri kümelerinde çalışmak için iyidir.

Buna iyi bir örnek ORM veya veritabanı soyutlama katmanı olabilir. Birden çok veritabanı bağlantınız olabilir.

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

Bu iki bağlantı artık bağımsız olarak çalışabilir:

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

Şimdi bu paket / kütüphane içinde, SELECT ifadesinden döndürülen belirli bir satırı temsil etmek için Db_Row vb. Gibi başka sınıflar olabilir. Bu Db_Row sınıfı statik bir sınıf olsaydı, o zaman bir veritabanında yalnızca bir veri satırınız olduğunu varsayar ve bir nesne örneğinin yapabileceklerini yapmak imkansız olurdu. Bir örnekle, artık sınırsız sayıda veritabanında sınırsız sayıda tabloda sınırsız sayıda satıra sahip olabilirsiniz. Tek sınır sunucu donanımıdır;).

Örneğin, Db nesnesindeki getRows yöntemi bir Db_Row nesneleri dizisi döndürürse, artık her satırda birbirinden bağımsız olarak çalışabilirsiniz:

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();
}

Statik sınıfın iyi bir örneği, kullanıcı başına yalnızca bir kayıt defteri veya bir oturum olacağından kayıt defteri değişkenlerini veya oturum değişkenlerini işleyen bir şey olabilir.

Başvurunuzun bir bölümünde:

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

Ve başka bir bölümde:

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

Oturum başına bir seferde yalnızca bir kullanıcı olacağından, Oturum için örnek oluşturmanın bir anlamı yoktur.

Umarım bu yardımcı olur, diğer cevaplarla birlikte işleri temizlemeye yardımcı olur. Bir yan not olarak, " uyum " ve " bağlantı " kontrol edin . Kodunuzu yazarken tüm programlama dilleri için geçerli olan bazı çok, çok iyi uygulamaları ana hatlarıyla belirtirler.


6

Sınıfınız statikse, bu, nesnesini diğer sınıflara geçiremeyeceğiniz anlamına gelir (olası bir örnek olmadığından), bu da tüm sınıflarınızın doğrudan bu statik sınıfı kullanacağı anlamına gelir; .

Sıkı bağlantı, kodunuzu daha az yeniden kullanılabilir, kırılgan ve hatalara yatkın hale getirir. Statik sınıfların, sınıfın örneğini diğer sınıflara geçirmesini önlemek istiyorsunuz.

Ve evet, bu, bazıları daha önce bahsedilen diğer birçok nedenden sadece bir tanesidir.


3

Genel olarak, tüm değişkenler arasında kesinlikle paylaşılması gerekmedikçe veya tek birton oluşturmadığınız sürece üye değişkenleri ve üye işlevlerini kullanmalısınız. Üye verilerini ve üye işlevlerini kullanmak, işlevlerinizi birden çok farklı veri parçası için yeniden kullanmanızı sağlarken, statik veri ve işlevleri kullanırsanız üzerinde çalıştığınız verilerin yalnızca bir kopyasına sahip olabilirsiniz. Ayrıca, PHP için geçerli olmasa da, statik işlevler ve veriler kodun yeniden girilememesine neden olurken, sınıf verileri yeniden girintiyi kolaylaştırır.


3

Çapraz dil uygulamalarında kesinlikle statik değişkenlerin olmasını istediğim bir durum olduğunu söylemek istiyorum. Bir dili geçirdiğiniz bir sınıfa sahip olabilirsiniz (örneğin, $ _SESSION ['dil']) ve bu şekilde tasarlanan diğer sınıflara erişir:

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

Strings :: getString ("somestring") kullanmak, dil kullanımınızı uygulamanızdan soyutlamanın güzel bir yoludur. Ancak bunu yapabilirsiniz, ancak bu durumda her dizgi dosyası dizeleri sınıf tarafından erişilen dize değerlerine sahip sabitleri oldukça iyi çalışıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.