Bir sınıfın asıl sorumluluğu nedir?


42

OOP'deki isimlere dayanan fiilleri kullanmanın yasal olup olmadığını merak ediyorum.
Bu mükemmel makaleye rastladım , yine de yaptığı noktaya katılmıyorum.

Sorunu biraz daha açıklamak için makale, örneğin bir FileWritersınıf olmaması gerektiğini , ancak yazmanın bir eylem olduğu için sınıfın bir yöntemi olması gerektiğini belirtir File. Bir Java programcısı, bir Java programcısının kullanmayacağı bir Ruby programcısı muhtemelen bir FileWritersınıfın kullanımına (Ruby File.openbir dosyaya erişmek için yöntem kullanır) olacağından, genellikle dile bağlı olduğunu fark edeceksiniz .

Benim kişisel (ve evet, çok mütevazi) görüşüm, böyle yapmanın Tek Sorumluluk ilkesini bozacağı yönünde. PHP'de programlandığımda (PHP açıkça OOP için en iyi dil olduğu için değil mi?), Bu tür bir çerçeve kullanırdım:

<?php

// This is just an example that I just made on the fly, may contain errors

class User extends Record {

    protected $name;

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

}

class UserDataHandler extends DataHandler /* knows the pdo object */ {

    public function find($id) {
         $query = $this->db->prepare('SELECT ' . $this->getFields . ' FROM users WHERE id = :id');
         $query->bindParam(':id', $id, PDO::PARAM_INT);
         $query->setFetchMode( PDO::FETCH_CLASS, 'user');
         $query->execute();
         return $query->fetch( PDO::FETCH_CLASS );
    }


}

?>

Anladığım kadarıyla DataHandler sonekinin alakalı bir şey eklemediğini ; Fakat mesele şu ki, tek sorumluluk prensibi bize veri içeren bir model olarak kullanılan bir nesnenin (bir Kayıt olarak adlandırılabilir) SQL sorguları ve DataBase erişimi yapma sorumluluğuna sahip olmaması gerektiğini belirtiyor. Bu, bir şekilde, örneğin Ruby on Rails tarafından kullanılan ActionRecord modelini geçersiz kılar.

Geçen gün bu C # koduna (yay, bu yazıda kullanılan dördüncü nesne dili) rastladım:

byte[] bytes = Encoding.Default.GetBytes(myString);
myString = Encoding.UTF8.GetString(bytes);

Ve şunu söylemeliyim ki, bir Encodingveya Charsetsınıfın gerçekte dizeleri kodlamasının bir anlamı yoktur . Sadece bir kodlamanın gerçekte ne olduğunun bir temsili olmalıdır.

Böylece, şunu düşünme eğiliminde olurdum:

  • FileDosyaları açmak, okumak veya kaydetmek bir sınıf sorumluluk değildir .
  • Kendini Xmlserileştirmek sınıf sorumluluğu değildir .
  • UserVeritabanını sorgulamak sınıf sorumluluğu değildir .
  • vb.

Ancak, bu fikirleri ileri sürdüğümüzde, neden Objectbir toStringsınıf olsun? Kendini bir dizgeye dönüştürmek bir Araba ya da Köpeğin sorumluluğu değil, değil mi?

Pragmatik bir bakış açısıyla, toStringkatı bir SOLID formunu izlemenin güzelliği için yöntemden kurtulmanın, kodun işe yaramaz hale getirilmesiyle daha sürdürülebilir olmasını sağlayan bir seçenek olmadığını kabul ediyorum.

Ayrıca, bunun için kesin bir cevap ( ciddi bir cevaptan ziyade bir deneme olacaktır ) veya görüşe dayalı olabileceğini de anlıyorum. Yine de yaklaşımımın aslında tek sorumluluk ilkesinin gerçekte ne olduğunu takip edip etmediğini bilmek istiyorum.

Bir sınıfın sorumluluğu nedir?


ToString nesneleri çoğunlukla kolaylık sağlar ve toString, hata ayıklama sırasında iç durumu hızlı bir şekilde görüntülemek için genellikle kullanılır
cırcır ucubesi

3
@ ratchetfreak Katılıyorum, ancak tek sorumluluğun tanımladığım şekilde kırıldığını gösteren bir örnek (neredeyse şaka) oldu.
Pierre Arlaud

8
Tek sorumluluk ilkesinin ateşli destekçileri olsa da, bence tasarım seçimlerinin ezici çoğunluğu için en iyi şekilde bir rehber ve çok gevşek bir fikir. SRP ile ilgili sorun, yüzlerce garip bir şekilde adlandırılmış sınıflarla bitiyor olmanız, ihtiyacınız olanı bulmayı (veya ihtiyacınız olanın zaten var olup olmadığını öğrenmek) ve büyük resmi kavramanızı gerçekten zorlaştırıyor. Çok az sorumluluğu olsa bile, onlardan ne bekleyeceğimi söyleyen daha az iyi adlandırılmış sınıflara sahip olmayı tercih ederim. Gelecekte işler değişirse o zaman sınıfı bölerim.
Dunk

Yanıtlar:


24

Diller arasındaki bazı farklılıklar göz önüne alındığında, bu zor bir konu olabilir. Bu nedenle, aşağıdaki yorumları OO dünyasında olabildiğince kapsamlı olmaya çalışacak şekilde formüle ediyorum.

Her şeyden önce, "Tek Sorumluluk İlkesi" olarak adlandırılan, kavram uyumu için açıkça ilan edilen bir refleksdir . Zamanın literatürünü okumak (yaklaşık 70 civarında), insanlar bir modülün ne olduğunu ve onları iyi özellikleri koruyacak şekilde nasıl inşa etmeyi tanımlamakta zorlanıyorlardı. Bu yüzden, “burada bir sürü yapı ve prosedür var, onlardan bir modül yapacağım” diyeceklerdi, ancak bu keyfi küme kümesinin neden birlikte paketlendiğine dair hiçbir kriter olmadan örgüt çok az anlam ifade edebilir. - küçük "uyum". Dolayısıyla, kriterler üzerine tartışmalar ortaya çıkmıştır.

Bu yüzden, burada dikkat edilmesi gereken ilk şey, şimdiye kadar tartışmanın örgütlenme ve bakım ve anlaşılabilirlik üzerindeki ilgili etkileri (bir modül "mantıklı" ise, küçük bir bilgisayar için).

Sonra bir başkası (Bay Martin) geldi ve aynı düşünceyi, sınıfın birimlerine, ne olması gerekip ne olmaması gerektiğini düşünürken kullanmak için bir kriter olarak uyguladı. İşte. Yaptığı nokta "Bir sınıfın değişmesi için tek bir sebep olmalı" idi .

Eh, deneyimden biliyoruz ki "birçok şeyi" yapmış gibi görünen pek çok nesnenin (ve birçok sınıf) bunu yapmak için çok iyi bir nedeni var. İstenmeyen durum, bakım vb. İçin dayanılmaz bir noktaya kadar işlevselliği ile şişirilmiş sınıflar olacaktır. Ve ikincisini anlamak, Bay. Martin, konuyu ne zaman hazırladığını hedefliyordu.

Tabii ki, ne okuduktan sonra Bay. Martin, sorunlu senaryolardan kaçınmak için bunların yönlendirme ve tasarım kriterleri olduğu açıkça belirtilmelidir , herhangi bir şekilde bir uyumu sürdürmek için değil, özellikle "sorumluluk" tanımlandığında (ve " ilkesini ihlal ediyor? "" yaygın kargaşanın mükemmel örnekleridir). Böylece, talihsiz buluyorum bir prensip denirİnsanları yanıltmak, son sonuçlara götürmeye çalışmakta, hiçbir işe yaramayacaktır. Bay Martin, muhtemelen ayrılık daha kötü sonuçlar doğuracağından, muhtemelen bu şekilde tutulması gereken "birden fazla şey yapan" tasarımları tartıştı. Ayrıca, modülerlikle ilgili bilinen birçok zorluk vardır (ve bu konu bunun bir örneğidir), bununla ilgili bazı basit sorular için bile iyi cevaplar alma noktasında değiliz.

Bununla birlikte, bu fikirleri tahmin edersek, neden Object'in toString sınıfına sahip olması gerekir? Kendisini bir dizgeye dönüştürmek bir Araba ya da Köpeğin sorumluluğu değildir, değil mi?

Şimdi, burada toStringşu konuda bir şeyler söylemeyi bırakmama izin verin : bir kişi bu düşünce modüllerinden sınıflara geçişi ve hangi yöntemlerin bir sınıfa ait olması gerektiği üzerinde düşünürken ihmal edilen temel bir şey var. Ve olay dinamik gönderme (aka, geç bağlanma, "polimorfizm").

"Geçersiz kılma yöntemleri" olmayan bir dünyada, "obj.toString ()" veya "toString (obj)" arasında seçim yapmak tek başına bir sözdizimi tercihi meselesidir. Bununla birlikte, programcıların mevcut / geçersiz kılınan bir yöntemin farklı bir uygulamasıyla bir alt sınıf ekleyerek bir programın davranışını değiştirebileceği bir dünyada, bu seçenek artık bir zevk değil: prosedürü geçersiz kılmak için aday kılabilir; ve aynısı “özgür prosedürler” için doğru olmayabilir (çoklu metotları destekleyen dillerin bu ikilikten bir çıkışı vardır). Sonuç olarak, bu sadece organizasyon hakkında değil, anlambilim üzerinde de bir tartışmadır. Son olarak, yöntemin hangi sınıfa bağlı olduğu, aynı zamanda etkileyici bir karar haline gelir (ve çoğu durumda, şu ana kadar, olayların nereye ait olduğuna karar vermemize yardımcı olacak kılavuzlardan daha azına sahibiz,

Sonunda, örneğin her küçük şey için bir sınıf oluşturmaya zorlayan, korkunç tasarım kararları taşıyan dillerle karşı karşıyayız. Bu nedenle, bir zamanlar kanonik neden ve nesnelerin (ve sınıf-sınıfta, dolayısıyla sınıfların) hepsinin, yani "veri gibi davranan" davranışlara sahip olan bu "nesnelere" sahip olmasının temel kriterleri neydi? Ancak, somut temsillerini (varsa) her ne pahasına olursa olsun doğrudan manipülasyondan koruyun (ve bu, bir nesnenin arayüzü olması gerektiği için, müşterileri açısından neyin temel olduğu konusundaki ana ipucu) bulanıklaşır ve karışır.


31

[Not: Burada nesneler hakkında konuşacağım. Nesneler, nesne yönelimli programlamanın nihayetinde sınıflardan ibaret olmadığıdır.]

Bir nesnenin sorumluluğunun ne olduğu çoğunlukla etki alanı modelinize bağlıdır. Aynı etki alanını modellemenin genellikle birçok yolu vardır ve sistemin nasıl kullanılacağına bağlı olarak bir yol veya diğerini seçersiniz.

Hepimizin bildiği gibi, başlangıç ​​kurslarında sıklıkla öğretilen "fiil / isim" metodolojisi saçmadır, çünkü bir cümleyi nasıl formüle ettiğinize çok bağlıdır. Aktif veya pasif bir sesle hemen hemen her şeyi bir isim veya bir fiil olarak ifade edebilirsiniz. Etki alanı modelinizin yanlış olmasına bağlı olarak, bir şeyin bir nesneyi ya da yöntemi olup olmadığını, sadece gereksinimleri nasıl formüle ettiğinizin kazara ortaya çıkmasının bir sonucu değil, bilinçli bir tasarım tercihi olmalıdır.

Ancak, yok İngilizce'de aynı şeyi ifade imkanı sunulmuştur tıpkı gösteriyor, etki alanı modeli aynı şeyi ifade eden birçok olanakları var ... ve bu olasılıklardan hiçbiri doğal olarak daha doğru diğerlerine göre. Bu koşullara bağlıdır.

İşte tanıtım OO kurslarında da oldukça popüler olan bir örnek: bir banka hesabı. Genellikle bir balancealan ve bir transferyöntem ile bir nesne olarak modellenmiştir . Başka bir deyişle: hesap bakiyesi veridir ve transfer bir işlemdir . Bu bir banka hesabını modellemenin makul bir yoludur.

Ancak, banka hesaplarının gerçek dünya bankacılık yazılımında modellenmesi değildir (ve aslında bankaların gerçek dünyada nasıl çalıştığı değildir). Bunun yerine, bir işlem fişiniz var ve hesap bakiyesi, bir hesap için tüm işlem fişlerini toplayarak (ve çıkararak) hesaplanır. Başka bir deyişle: transfer veridir ve denge bir işlemdir ! (İlginçtir ki, bu aynı zamanda sisteminizi tamamen işlevsel kılar, çünkü dengeyi hiçbir zaman değiştirmeniz gerekmez, hem hesap nesneleriniz hem de işlem nesnelerinin hiçbir zaman değişmesi gerekmez, bunlar değişmezdir.)

Hakkında özel sorunuza gelince toString, katılıyorum. Bir Showable tür sınıfının Haskell çözümünü çok tercih ederim . (Scala bize tip sınıflarının OO ile çok iyi uyduğunu öğretir.) Eşitlik ile aynı. Eşitlik çok sık olmayan bir nesnenin ancak nesne kullanıldığı bağlamda bir özelliği. Sadece kayan nokta sayılarını düşünün: epsilon ne olmalı? Yine Haskell Eqtip sınıfına sahip.


Haskell karşılaştırmalarını beğendim, cevabı biraz daha ... taze hale getiriyor. O zaman ActionRecordhem bir model hem de bir veritabanı istemi olmasına izin veren tasarımlar hakkında ne söylersiniz ? Bağlam ne olursa olsun, tek sorumluluk ilkesini ihlal ediyor gibi görünüyor.
Pierre Arlaud

5
“... çünkü bir cümleyi nasıl formüle ettiğinize çok bağlı.” Yuppi! Oh ve ben bankacılık örneğinizi çok seviyoruz. Seksenli yılların sonuna kadar işlevsel programlama dersi aldığımı asla bilmiyordum :) (dengeler vb. yeni bir gerçek) ...
Marjan Venema,

1
Fiil / İsim metodolojisinin "saçma" olduğunu söyleyemem. Basit tasarımların başarısız olacağını ve doğru olmanın çok zor olduğunu söyleyebilirim . Çünkü, karşı örnek, RESTful API bir Fiil / İsim metodolojisi değil mi? Ekstra bir zorluk derecesi için fiillerin son derece sınırlı olduğu yerlerde?
user949300,

@ user949300: Fiiller dizisinin sabit olması, bahsettiğim problemi kesin olarak önler , yani cümleleri çok farklı şekillerde formüle edebilmeniz, böylece bir isim ve bir fiil olanın etki alanı analizine değil yazma stiline bağlı olması. .
Jörg W Mittag

8

Sıklıkla Tek Sorumluluk Prensibi Sıfır Sorumluluk Prensibi olur ve bir isim felaketine yol açan, hiçbir şey yapmayan Anemik Sınıflar (ayarlayıcılar ve alıcılar hariç) ile sonuçlanır .

Asla mükemmel olamayacaksınız, ancak bir sınıfın çok azından biraz daha fazlasını yapması daha iyi.

Kodlama ile size örnekte, IMO, kesinlikle gereken kodlamak mümkün. Bunun yerine ne yapmalı? Sadece "utf" ismine sahip olmak sıfır sorumluluktur. Şimdi, belki isim Encoder olmalı. Fakat Konrad'ın dediği gibi, veriler (kodlayan) ve davranış (bunu yapan) birbirine aittir .


7

Bir sınıfın örneği bir kapatmadır. Bu kadar. Bu şekilde düşünürseniz, bakacağınız tüm iyi tasarlanmış yazılımlar doğru bakar ve kötü düşünülmüş tüm yazılımlar olmaz. Genişleteyim.

Bir dosyaya yazmak için bir şeyler yazmak istiyorsanız, belirtmeniz gereken her şeyi (işletim sistemine) düşünün: dosya adı, erişim yöntemi (okuma, yazma, ekleme), yazmak istediğiniz dize.

Böylece dosya adı (ve erişim yöntemi) ile bir File nesnesi oluşturursunuz. File nesnesi şimdi dosya adı üzerinden kapatılmıştır (bu durumda muhtemelen onu salt okunur / const değeri olarak almış olacaktır). File örneği şimdi sınıfta tanımlanan "write" yöntemine çağrı almaya hazır. Bu sadece bir dize argümanını alır, ancak yazma yönteminin gövdesinde, uygulamada, dosya adına (veya ondan oluşturulan dosya tanıtıcısına) erişim de vardır.

Bu nedenle, bir File sınıfı örneği bazı verileri bir tür bileşik blobun içine toplar, böylece bu örneğin kullanımı daha kolaydır. Bir File nesnesine sahip olduğunuzda, dosya adının ne olduğu konusunda endişelenmenize gerek yoktur, yalnızca yazma çağrısına argüman olarak hangi dizeyi koyduğunuzla ilgilenirsiniz. Yinelemek için - File nesnesi yazmak istediğiniz dizenin gerçekte nereye gittiği hakkında bilmeniz gerekmeyen her şeyi içerir.

Çözmek istediğiniz çoğu problem bu yolla - önceden yaratılan şeyler, bir tür işlem döngüsünün her yinelemesinin başlangıcında yaratılanlar, daha sonra bir if'in iki yarısında ilan edilen farklı şeyler, sonra yaratılanlar Bir tür alt döngünün başlangıcı, sonra da bu döngünün içinde algoritmanın bir parçası olarak ilan edilen şeyler vs. ilan edilir. Yığına giderek daha fazla işlev çağrısı eklediğinizde, daha ince taneli kodlar yaparsınız, ancak her zaman Yığındaki alt katmanlardaki verileri bir tür güzel soyutlamaya dönüştürerek erişimi kolaylaştırır. Ne yaptığınızı görün, işlevsel bir programlama yaklaşımı için "OO" yu bir tür kapama yönetimi çerçevesi olarak kullanıyor.

Bazı sorunların kısayol çözümü, çok işlevsel olmayan nesnelerin değişken durumlarını içerir - kapakları dışarıdan değiştiriyorsunuz. Sınıfınızdaki bazı şeyleri “küresel” yapıyorsunuz, en azından yukarıdaki kapsamda. Her zaman bir pasör yazdığın zaman bunlardan kaçınmaya çalış. Bu, sınıfınızın ya da çalıştığı diğer sınıfların sorumluluğunu aldığınız yerin yanlış olduğunu iddia ediyordum. Fakat çok fazla endişelenmeyin - bilişsel yükü çok fazla bilişsel yük olmadan çabucak, çok fazla bilişsel yük olmadan çözmek ve aslında bir şeyler yapmak için bazen değişken bir duruma getirmek pratiktir.

Özetle, sorunuza cevap vermek için - bir sınıfın asıl sorumluluğu nedir? Daha ayrıntılı bir operasyon türünü elde etmek için bazı temel verilere dayanarak bir tür platform sunmak. Verinin diğer yarısından daha az sıklıkta değişen bir işlemde yer alan verilerin yarısını kapsüllemek içindir (Düşünerek körleme ...). Örneğin, dosya adı vs dize yazmak.

Genellikle bu enkapsülasyonlar, yüzeysel olarak gerçek dünya nesnesi / problem alanındaki bir nesne gibi görünür fakat kanmayın. Bir Araba sınıfı oluşturduğunuzda, aslında bir araba değil, bir arabada yapmak isteyebileceğiniz belli türden şeyleri elde etmek için bir platform oluşturan bir veri topluluğudur. Bir dize temsili oluşturmak (toString) bu şeylerden biridir - tüm bu iç verileri tükürür. Unutmayın, sorun alanı tamamen arabalarla ilgili olsa bile, bazen bir Araba sınıfının doğru sınıf olmayabilir. İsimlerin Krallığı'nın aksine, bir sınıfın etrafında olması gereken işlemler, işlemler.


4

Ayrıca, bunun için kesin bir cevap (ciddi bir cevaptan ziyade bir deneme olacaktır) veya görüşe dayalı olabileceğini de anlıyorum. Yine de, yaklaşımımın aslında tek sorumluluk ilkesinin gerçekte ne olduğunu takip edip etmediğini bilmek istiyorum.

Olsa da, mutlaka iyi kod için yapmak olmayabilir. Herhangi bir kör takip kuralının kötü koda yol açtığı gerçeğinin ötesinde, geçersiz bir öncül başlatıyorsunuz: (programlama) nesneler (fiziksel) nesneler değil.

Nesneler, bir dizi veri ve işlem kümesidir (ve bazen ikisinden yalnızca biri). Bunlar çoğu zaman gerçek dünya nesnelerini modellerken, bir şeyin bilgisayar modeli ile bu şeyin kendisinin arasındaki fark, farklılıkları gerektirir .

Bir şeyi temsil eden bir "isim" ile onları tüketen diğer şeyler arasındaki bu sert çizgiyi alarak, nesne yönelimli programlamanın (fonksiyonun değişmeyenlerini koruyabilmesi için birlikte işlev ve durum olması) temel yararına karşı çıkacaksınız. . Daha da kötüsü, tarihin gösterdiği gibi fiziksel dünyayı kodda temsil etmeye çalışıyorsunuz.


4

Geçen gün bu C # koduna (yay, bu yazıda kullanılan dördüncü nesne dili) rastladım:

byte[] bytes = Encoding.Default.GetBytes(myString);
myString = Encoding.UTF8.GetString(bytes);

Ve şunu söylemeliyim ki, bir Encodingveya Charsetsınıfın gerçekte dizeleri kodlamasının bir anlamı yoktur . Sadece bir kodlamanın gerçekte ne olduğunun bir temsili olmalıdır.

Teoride, evet, ama C # 'nın basitlik uğruna haklı bir taviz verdiğini düşünüyorum (Java'nın daha katı bir yaklaşımının aksine).

EncodingSadece belirli bir kodlamayı (veya tanımlanmışsa) temsil ederse - UTF-8 - diyelim - o zaman açıkça bir Encodersınıfa ihtiyacınız olacak , böylece uygulayabileceksin GetBytes- ama sonra Encoders ve Encodings arasındaki ilişkiyi yönetmelisin. Sonunda iyi ol '

EncodersFactory.getDefaultFactory().createEncoder(new UTF8Encoding()).getBytes(myString)

o kadar zekice bağlandı o makalede (bu arada büyük bir okuma).

Seni iyi anlarsam çizginin nerede olduğunu soruyorsun.

Spektrumun diğer tarafında, statik sınıfların kullanımıyla OOP dillerinde uygulanması zor olmayan, usule dayalı bir yaklaşımdır:

HelpfulStuff.GetUTF8Bytes(myString)

Buradaki en büyük sorun, izninin yazarı HelpfulStuffve ben monitörün önüne oturduğumda, nerede GetUTF8Bytesuygulandığını bilmeme imkân yok .

Ben bunun için görünüyorum HelpfulStuff, Utils, StringHelper?

Eğer yöntem gerçekten üçünün hepsine bağımsız olarak uygulandıysa, bana yardımcı olan Lord ... çok şey olur, tek gereken, benden önceki adamın bu işlevi nerede arayacağını bilmediği ve hiçbir şeyin olmadığına inandığıdır. Yine de başka bir tane çıkardılar ve şimdi onları bolca ele geçirdik.

Bana göre, uygun tasarım soyut kriterleri yerine getirmekle ilgili değil, kodunuzun önünde oturduktan sonra devam etmeyi kolaylaştırıyor. Şimdi nasıl

EncodersFactory.getDefaultFactory().createEncoder(new UTF8Encoding()).getBytes(myString)

bu açıdan oranı? Zavallı olarak da. Çalışma arkadaşıma bağırdığımda duymak istediğim bir cevap değil, "bir dizgiyi bayt dizisine nasıl kodlarım?" :)

Bu yüzden ılımlı bir yaklaşımla gideceğim ve tek bir sorumluluk konusunda liberal olacağım. EncodingSınıfın hem kodlama tipini tanımlamasını hem de gerçek kodlamayı yapmasını tercih ederim : davranıştan ayrılmamış veriler.

Sanırım, dişlileri yağlamaya yardımcı olan bir cephe kalıbı olarak geçiyor. Bu meşru patern SOLID'i ihlal ediyor mu? İlgili bir soru: Cephe düzeni SRP'yi ihlal ediyor mu?

Bunun kodlanmış baytların dahili olarak nasıl uygulanabileceğini unutmayın (.NET kitaplıklarında). Sınıflar halka açık görünmüyor ve sadece bu "facadeous" API'si dışarıda gösteriliyor. Doğru olup olmadığını doğrulamak zor değil, şu anda yapmak için biraz tembelim :) Muhtemelen öyle değil, ama bu benim açımdan geçersiz kılmaz.

Dahili olarak daha şiddetli ve kanonik bir uygulama sürdürürken, arkadaşlık uğruna herkes tarafından görülebilen bir uygulamayı basitleştirmeyi seçebilirsiniz.


1

Java'da dosyaları temsil etmek için kullanılan soyutlama bir akıştır, bazı akışlar tek yönlüdür (sadece oku veya sadece yaz) diğerleri diğerleri çift yönlüdür. Ruby için File sınıfı, OS dosyalarını temsil eden soyutlamadır. Öyleyse soru, onun tek sorumluluğu nedir? Java'da, FileWriter'ın sorumluluğu bir işletim sistemine bağlı olan tek yönlü bir bayt akışı sağlamaktır. Ruby'de, File'ın sorumluluğu bir sistem dosyasına çift yönlü erişim sağlamaktır. İkisi de SRP'yi yerine getiriyor, sadece farklı sorumlulukları var.

Kendisini bir dizgeye dönüştürmek bir Araba ya da Köpeğin sorumluluğu değildir, değil mi?

Tabii neden olmasın? Araba sınıfının bir Araba soyutlamasını tamamen temsil etmesini bekliyorum. Bir dizgenin gerekli olduğu yerde araba soyutlamanın kullanılması mantıklıysa, o zaman tamamen Car sınıfının bir dizgeye dönüşümü desteklemesini bekliyorum.

Daha büyük soruya doğrudan cevap vermek, bir sınıfın sorumluluğunda bir soyutlama yapmaktır. Bu iş karmaşık olabilir, ancak bu bir nokta olabilir; bir soyutlama, karmaşık şeyleri kullanımı kolay, daha az karmaşık bir arayüzün arkasına gizlemelidir. SRP bir tasarım rehberidir, bu nedenle “bu soyutlamayı neyi temsil ediyor?” Sorusuna odaklanıyorsunuz. Eğer kavramı tamamen soyutlamak için çok sayıda farklı davranış gerekliyse, öyle olsun.


0

Sınıfın birden fazla sorumluluğu olabilir. En azından klasik veri merkezli OOP'de.

Tek sorumluluk ilkesini tam olarak uyguluyorsanız, temelde her yardımcı olmayan yöntemin kendi sınıfına ait olduğu, sorumluluk odaklı bir tasarıma sahip olursunuz .

File ve FileWriter örneğinde olduğu gibi bir temel sınıftan karmaşıklık parçasını çıkarmanın yanlış bir şey olmadığını düşünüyorum. Bunun avantajı, belirli bir işlemden sorumlu olan kodun açıkça ayrılmasını sağlamanız ve ayrıca tüm temel sınıfı (örn. Dosya) geçersiz kılmak yerine yalnızca bu kod parçasını geçersiz kılabileceğinizdir (alt sınıf). Yine de, bunu sistematik olarak uygulamak benim için bir onur gibi görünüyor. Kullanmanız gereken çok daha fazla sınıf elde edersiniz ve her işlem için ilgili sınıfını çağırmanız gerekir. Bazı maliyetlerle gelen esneklik.

Çıkartılan bu sınıflar içerdikleri operasyonda, örneğin dayalı çok açıklayıcı isimlere sahip olmalıdır <X>Writer, <X>Reader, <X>Builder. Gibi isimler <X>Manager, <X>Handlerişlemi hiç tanımlamaz ve birden fazla işlem içerdikleri için böyle çağrılırlarsa, ne elde ettiğiniz bile belli değildir. İşlevselliği verilerinden ayırdınız, o ayıklanan sınıfta bile tek bir sorumluluk ilkesini çiğnediniz ve belirli bir yöntem arıyorsanız, nerede arayacağınızı bilemeyebilirsiniz (eğer <X>veya varsa <X>Manager).

Şimdi, UserDataHandler'daki durumunuz farklı çünkü sınıf yok (gerçek veri içeren sınıf). Bu sınıfın verileri harici bir kaynakta (veritabanı) depolanır ve bunlara erişmek ve bunları işlemek için bir api kullanıyorsunuz. Kullanıcı sınıfınız bir çalışma zamanı kullanıcısını temsil eder; kalıcı bir kullanıcıdan gerçekten farklı bir şeydir ve sorumlulukların ayrılması (kaygıları) burada özellikle çok fazla çalışma zamanı kullanıcısı ile ilgili mantığınız varsa kullanışlı olabilir.

Muhtemelen UserDataHandler olarak adlandırdınız çünkü bu sınıfta temelde sadece işlevsellik var ve gerçek veri yok. Ancak, bu durumdan da soyutlanabilir ve veritabanındaki verileri sınıfa ait olarak düşünebilirsiniz. Bu soyutlama ile sınıfa ne olduğuna bağlı olarak ismini verebilirsiniz . Ona oldukça kaygan olan ve aynı zamanda depo deseni kullanımını öneren UserRepository gibi bir ad verebilirsiniz .

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.