อะไรคือความรับผิดชอบที่แท้จริงของชั้นเรียน


42

ฉันสงสัยอยู่ตลอดว่าจะใช้กริยาที่ถูกต้องตามคำนามใน OOP หรือไม่
ฉันเจอบทความที่ยอดเยี่ยมนี้แต่ฉันก็ยังไม่เห็นด้วยกับประเด็นที่ทำ

เพื่ออธิบายปัญหาที่เกิดขึ้นอีกเล็กน้อยรัฐบทความที่ว่ามีไม่ควรจะเป็นตัวอย่างเช่นFileWriterชั้น แต่เนื่องจากการเขียนคือการกระทำที่มันควรจะเป็นวิธีการFileของการเรียน คุณจะได้รับรู้ว่ามันมักจะขึ้นอยู่กับภาษาเนื่องจากโปรแกรมเมอร์ Ruby อาจต่อต้านการใช้FileWriterคลาส (Ruby ใช้วิธีFile.openการเข้าถึงไฟล์) ในขณะที่โปรแกรมเมอร์ Java ไม่ต้องการ

มุมมองส่วนบุคคลของฉัน (และใช่ต่ำต้อยมาก) คือการทำเช่นนั้นจะทำลายหลักการความรับผิดชอบเดี่ยว เมื่อฉันตั้งโปรแกรมใน PHP (เพราะเห็นได้ชัดว่า PHP เป็นภาษาที่ดีที่สุดสำหรับ OOP ใช่มั้ย) ฉันมักจะใช้เฟรมเวิร์กประเภทนี้:

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


}

?>

มันเป็นความเข้าใจของฉันที่ DataHandler ต่อท้ายไม่ได้เพิ่มอะไรที่เกี่ยวข้อง ; แต่ประเด็นก็คือหลักการความรับผิดชอบเดี่ยวบอกเราว่าวัตถุที่ใช้เป็นแบบจำลองที่มีข้อมูล (อาจเรียกว่า Record) ไม่ควรมีความรับผิดชอบในการทำแบบสอบถาม SQL และการเข้าถึงฐานข้อมูล วิธีนี้จะทำให้รูปแบบ ActionRecord ที่ใช้สำหรับ Ruby on Rails ไม่ถูกต้อง

ฉันเจอรหัส C # นี้ (ภาษาอยุ่, ภาษาวัตถุที่สี่ที่ใช้ในโพสต์นี้) เมื่อวันก่อน:

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

และฉันต้องบอกว่ามันไม่สมเหตุสมผลกับฉันมากนักว่าคลาสEncodingหรือCharsetคลาสจะเข้ารหัสสตริง มันควรจะเป็นตัวแทนของสิ่งที่เข้ารหัสจริงๆ

ดังนั้นฉันมักจะคิดว่า:

  • ไม่ใช่Fileความรับผิดชอบของคลาสในการเปิดอ่านหรือบันทึกไฟล์
  • ไม่ใช่Xmlความรับผิดชอบของคลาสในการทำให้เป็นอนุกรม
  • ไม่ใช่Userความรับผิดชอบของคลาสในการสืบค้นฐานข้อมูล
  • เป็นต้น

อย่างไรก็ตามถ้าเราประเมินความคิดเหล่านี้ทำไมจะObjectมีtoStringชั้นเรียน มันไม่ใช่ความรับผิดชอบของ Car หรือ Dog ในการแปลงตัวมันเองให้เป็นสตริงตอนนี้ใช่ไหม?

ฉันเข้าใจว่าจากมุมมองในทางปฏิบัติการกำจัดtoStringวิธีเพื่อความสวยงามในการทำตามรูปแบบโซลิดที่เข้มงวดซึ่งทำให้โค้ดสามารถบำรุงรักษาได้มากขึ้นโดยทำให้ไร้ประโยชน์ไม่ใช่ตัวเลือกที่ยอมรับได้

ฉันเข้าใจด้วยว่าอาจไม่มีคำตอบที่แน่นอน (ซึ่งจะเป็นบทความมากกว่าคำตอบที่จริงจัง) สำหรับเรื่องนี้หรืออาจเป็นไปตามความคิดเห็น อย่างไรก็ตามฉันยังต้องการทราบว่าแนวทางของฉันเป็นไปตามหลักการความรับผิดชอบเดี่ยวหรือไม่

ความรับผิดชอบของชั้นเรียนคืออะไร?


อ็อบเจกต์ toString นั้นสะดวกสบายเป็นส่วนใหญ่และโดยทั่วไปแล้ว toString จะใช้เพื่อดูสถานะภายในระหว่างการดีบักอย่างรวดเร็ว
ratchet freak

3
@ ratchetfreak ฉันเห็นด้วย แต่มันก็เป็นตัวอย่าง (เกือบจะเป็นเรื่องตลก) ที่จะแสดงว่าความรับผิดชอบเดียวมักจะถูกทำลายในแบบที่ฉันอธิบาย
Pierre Arlaud

8
ในขณะที่หลักการความรับผิดชอบเดี่ยวมีผู้สนับสนุนที่กระตือรือร้น แต่ฉันคิดว่ามันเป็นแนวทางที่ดีที่สุดและเป็นเรื่องที่หลวมมากสำหรับการเลือกการออกแบบส่วนใหญ่ ปัญหาของ SRP คือคุณจบด้วยคลาสที่มีชื่อแปลก ๆ หลายร้อยคลาสมันทำให้ยากที่จะค้นหาสิ่งที่คุณต้องการ (หรือดูว่าสิ่งที่คุณต้องการมีอยู่แล้ว) และเข้าใจภาพรวมขนาดใหญ่ ฉันชอบที่จะมีชั้นเรียนที่มีชื่อน้อยกว่ามากซึ่งบอกฉันทันทีว่าจะคาดหวังอะไรจากพวกเขาแม้ว่าพวกเขาจะมีความรับผิดชอบค่อนข้างน้อย หากในอนาคตสิ่งต่าง ๆ เปลี่ยนแปลงแล้วฉันจะแยกชั้น
Dunk

คำตอบ:


24

เมื่อพิจารณาจากความแตกต่างระหว่างภาษาสิ่งนี้อาจเป็นหัวข้อที่ยุ่งยาก ดังนั้นฉันจึงกำหนดข้อคิดเห็นต่อไปนี้ในวิธีที่พยายามที่จะครอบคลุมให้มากที่สุดเท่าที่จะทำได้ภายในขอบเขตของ OO

ครั้งแรกของทั้งหมดที่เรียกว่า "Single รับผิดชอบหลักการ" คือการสะท้อน - ประกาศอย่างชัดเจน - ของแนวคิดการทำงานร่วมกัน การอ่านวรรณกรรมของเวลา (ประมาณ '70) ผู้คน (และยังคง) กำลังดิ้นรนเพื่อกำหนดว่าโมดูลคืออะไรและวิธีการสร้างพวกเขาในวิธีที่จะรักษาคุณสมบัติที่ดี ดังนั้นพวกเขาจะพูดว่า "นี่คือโครงสร้างและขั้นตอนมากมายฉันจะสร้างโมดูลจากพวกเขา" แต่ไม่มีเกณฑ์ว่าทำไมชุดสิ่งต่าง ๆ นี้เข้าด้วยกันองค์กรอาจจบลงด้วยความรู้สึกเล็กน้อย - "การทำงานร่วมกัน" เล็กน้อย ดังนั้นการอภิปรายเกี่ยวกับเกณฑ์ที่เกิดขึ้น

ดังนั้นสิ่งแรกที่ควรทราบที่นี่คือจนถึงการอภิปรายรอบองค์กรและผลกระทบที่เกี่ยวข้องกับการบำรุงรักษาและความเข้าใจ (สำหรับเรื่องเล็ก ๆ น้อย ๆ กับคอมพิวเตอร์หากโมดูล "เหมาะสม")

จากนั้นมีคนอื่น (นายมาร์ติน) เข้ามาและใช้ความคิดเดียวกันกับหน่วยการเรียนเป็นเกณฑ์ในการใช้เมื่อคิดถึงสิ่งที่ควรหรือไม่ควรเป็นของมันการส่งเสริมเกณฑ์นี้กับหลักการ ที่นี่ จุดที่เขาทำก็คือว่า"ชั้นควรจะมีเพียงหนึ่งเหตุผลที่จะเปลี่ยน"

เรารู้จากประสบการณ์ว่าวัตถุมากมาย (และหลายคลาส) ที่ดูเหมือนจะทำ "หลายสิ่ง" มีเหตุผลที่ดีมากสำหรับการทำเช่นนั้น กรณีที่ไม่พึงประสงค์จะเป็นชั้นเรียนที่เต็มไปด้วยฟังก์ชั่นจนถึงจุดที่ไม่ยอมรับการบำรุงรักษา ฯลฯ และเพื่อทำความเข้าใจหลังคือการดูที่นาย มาร์ตินตั้งเป้าเมื่อตอนที่เขาทำรายละเอียดในเรื่องนี้

แน่นอนหลังจากอ่านสิ่งที่นาย มาร์ตินเขียนควรมีความชัดเจนว่าสิ่งเหล่านี้เป็นเกณฑ์สำหรับทิศทางและการออกแบบเพื่อหลีกเลี่ยงสถานการณ์ที่มีปัญหาไม่ใช่วิธีการใด ๆ ในการปฏิบัติตามกฎระเบียบใด ๆ ละเมิดหลักการหรือไม่ "เป็นตัวอย่างที่สมบูรณ์แบบของความสับสนอย่างกว้างขวาง) ดังนั้นฉันพบว่ามันโชคร้ายที่มันถูกเรียกว่าหลักการทำให้ผู้คนเข้าใจผิดพยายามที่จะนำไปสู่ผลที่ตามมาในที่สุดซึ่งมันจะไม่ดี มิสเตอร์มาร์ตินพูดคุยเกี่ยวกับการออกแบบว่า "ทำมากกว่าหนึ่งอย่าง" ซึ่งน่าจะเป็นเช่นนั้นเพราะการแยกจะทำให้ได้ผลลัพธ์ที่แย่ลง นอกจากนี้ยังมีความท้าทายที่รู้จักมากมายเกี่ยวกับโมดุล (และเรื่องนี้เป็นเรื่องของมัน) เราไม่ได้มีคำตอบที่ดีแม้สำหรับคำถามง่ายๆเกี่ยวกับมัน

อย่างไรก็ตามหากเราประเมินความคิดเหล่านี้ทำไม Object จึงมีคลาส toString มันไม่ใช่ความรับผิดชอบของ Car หรือ Dog ในการแปลงตัวมันเองให้เป็นสตริงตอนนี้ใช่ไหม?

ตอนนี้ฉันขอหยุดพูดบางสิ่งที่นี่เกี่ยวกับtoString: มีสิ่งพื้นฐานที่ถูกทอดทิ้งโดยทั่วไปเมื่อมีการเปลี่ยนความคิดจากโมดูลเป็นคลาสและสะท้อนวิธีการที่ควรเป็นของคลาส และสิ่งนี้คือการจัดส่งแบบไดนามิก (aka, การผูกปลาย, "polymorphism")

ในโลกที่ไม่มี "วิธีการเอาชนะ" การเลือกระหว่าง "obj.toString ()" หรือ "toString (obj)" เป็นเรื่องของการตั้งค่าไวยากรณ์เพียงอย่างเดียว อย่างไรก็ตามในโลกที่โปรแกรมเมอร์สามารถเปลี่ยนพฤติกรรมของโปรแกรมโดยการเพิ่มคลาสย่อยที่มีการใช้งานที่แตกต่างกันของวิธีการที่มีอยู่ / แทนที่วิธีการเลือกนี้ไม่มีรสนิยมมากขึ้น: การทำขั้นตอนวิธียังสามารถทำให้ผู้สมัครเอาชนะ และสิ่งเดียวกันอาจไม่เป็นความจริงสำหรับ "ขั้นตอนฟรี" (ภาษาที่รองรับหลายวิธีมีทางออกจากการแบ่งขั้วนี้) ดังนั้นจึงไม่มีการอภิปรายเกี่ยวกับองค์กรเพียงอย่างเดียว แต่เกี่ยวกับความหมายเช่นกัน ในที่สุดวิธีการที่คลาสนั้นถูกผูกไว้ก็กลายเป็นการตัดสินใจที่ส่งผลกระทบ (และในหลาย ๆ กรณีจนถึงตอนนี้เรามีแนวทางมากกว่าที่จะช่วยเราตัดสินใจว่าสิ่งใดเป็นของตัวเอง

ในที่สุดเราต้องเผชิญกับภาษาที่มีการตัดสินใจในการออกแบบที่แย่มากเช่นบังคับให้คนหนึ่งสร้างคลาสสำหรับทุกสิ่งเล็กน้อย ดังนั้นสิ่งที่ครั้งหนึ่งเคยเป็นเหตุผลที่ยอมรับได้และเกณฑ์หลักในการมีวัตถุ (และในคลาส - ที่ดินดังนั้นคลาส) เลยก็คือการมี "วัตถุ" เหล่านี้ซึ่งเป็น "พฤติกรรมที่ทำหน้าที่เหมือนข้อมูล" แต่ปกป้องตัวแทนที่เป็นรูปธรรมของพวกเขา (ถ้ามี) จากการจัดการโดยตรงที่ค่าใช้จ่ายทั้งหมด (และนั่นคือคำแนะนำหลักสำหรับสิ่งที่ควรจะเป็นอินเทอร์เฟซของวัตถุจากมุมมองของลูกค้า) ทำให้เบลอและสับสน


31

[หมายเหตุ: ฉันจะพูดถึงวัตถุที่นี่ วัตถุคือสิ่งที่เกี่ยวกับการเขียนโปรแกรมเชิงวัตถุหลังจากทั้งหมดไม่ใช่คลาส]

ความรับผิดชอบของวัตถุขึ้นอยู่กับรุ่นของโดเมนเป็นส่วนใหญ่ โดยทั่วไปมีหลายวิธีในการสร้างแบบจำลองโดเมนเดียวกันและคุณจะเลือกหนึ่งวิธีหรืออื่น ๆ ตามวิธีที่ระบบจะใช้งาน

ดังที่เราทุกคนรู้ว่า "กริยา / คำนาม" วิธีการที่มักจะสอนในหลักสูตรเบื้องต้นนั้นเป็นเรื่องน่าขันเพราะมันขึ้นอยู่กับว่าคุณกำหนดประโยคอย่างไร คุณสามารถแสดงเกือบทุกอย่างไม่ว่าจะเป็นคำนามหรือคำกริยาไม่ว่าจะเป็นเสียงที่ใช้งานหรือเสียงที่ไม่โต้ตอบ การมีรูปแบบโดเมนของคุณขึ้นอยู่กับสิ่งที่ผิดมันควรจะเป็นตัวเลือกการออกแบบที่ใส่ใจไม่ว่าจะเป็นวัตถุหรือวิธีการไม่ใช่เพียงแค่ผลลัพธ์ที่เกิดขึ้นโดยไม่ตั้งใจจากวิธีที่คุณกำหนดความต้องการ

แต่ก็ไม่แสดงให้เห็นว่าเช่นเดียวกับที่คุณมีความเป็นไปได้จำนวนมากของการแสดงในสิ่งเดียวกันในภาษาอังกฤษคุณจะมีความเป็นไปได้จำนวนมากของการแสดงในสิ่งเดียวกันในรูปแบบโดเมนของคุณ ... และไม่มีความเป็นไปได้นั้นคือเนื้อแท้ที่ถูกต้องมากขึ้นกว่าคนอื่น ๆ มันขึ้นอยู่กับบริบท

นี่คือตัวอย่างที่เป็นที่นิยมอย่างมากในหลักสูตร OO เบื้องต้น: บัญชีธนาคาร ซึ่งโดยทั่วไปแล้วจะสร้างแบบจำลองเป็นวัตถุที่มีbalanceเขตข้อมูลและtransferวิธีการ ในคำอื่น ๆ : ยอดเงินในบัญชีเป็นข้อมูลและโอนเป็นการดำเนินงาน ซึ่งเป็นวิธีที่เหมาะสมในการสร้างแบบจำลองบัญชีธนาคาร

ยกเว้นนั่นไม่ใช่วิธีการสร้างแบบจำลองบัญชีธนาคารในซอฟต์แวร์ธนาคารในโลกแห่งความเป็นจริง (และในความเป็นจริงมันไม่ใช่วิธีการทำงานของธนาคารในโลกแห่งความเป็นจริง) แต่คุณมีสลิปธุรกรรมและยอดเงินคงเหลือในบัญชีจะถูกคำนวณโดยการเพิ่ม (และลบ) รายการสลิปทั้งหมดสำหรับบัญชี กล่าวอีกนัยหนึ่ง: การถ่ายโอนคือข้อมูลและยอดคงเหลือเป็นการดำเนินการ ! (ที่น่าสนใจสิ่งนี้ยังทำให้ระบบของคุณทำงานได้อย่างหมดจดเนื่องจากคุณไม่จำเป็นต้องแก้ไขยอดคงเหลือทั้งวัตถุบัญชีของคุณและวัตถุธุรกรรมที่ไม่จำเป็นต้องเปลี่ยนแปลงจึงไม่เปลี่ยนแปลง)

สำหรับคำถามเฉพาะของคุณเกี่ยวกับtoStringฉันเห็นด้วย ฉันชอบโซลูชัน Haskell ของShowable คลาสประเภทมาก (สกาลาสอนเราว่าคลาสของประเภทพอดีกับ OO อย่างสวยงาม) มีความเท่าเทียมกัน ความเสมอภาคนั้นมักจะไม่ใช่คุณสมบัติของวัตถุ แต่เป็นบริบทที่มีการใช้วัตถุนั้น แค่คิดเกี่ยวกับตัวเลขทศนิยม: เอปไซลอนควรเป็นอย่างไร อีกครั้ง Haskell มีEqคลาสประเภท


ฉันชอบการเปรียบเทียบฮาเซลของคุณมันทำให้คำตอบมากขึ้น…สด คุณจะพูดอะไรเกี่ยวกับการออกแบบที่อนุญาตให้ActionRecordทั้งสองเป็นแบบจำลองและผู้ร้องขอฐานข้อมูล ไม่ว่าบริบทมันจะทำลายหลักการความรับผิดชอบเดียว
Pierre Arlaud

5
"... เพราะมันขึ้นอยู่กับว่าคุณกำหนดประโยคอย่างไรมาก" เย้! โอ้และฉันชอบตัวอย่างการธนาคารของคุณ ไม่เคยรู้เลยว่าในยุคแปดสิบฉันได้สอนการเขียนโปรแกรมฟังก์ชั่น :) (การออกแบบศูนย์กลางข้อมูลและการจัดเก็บที่ไม่ขึ้นกับเวลาที่ยอดคงเหลือ ฯลฯ คำนวณได้และไม่ควรเก็บไว้และไม่ควรเปลี่ยนที่อยู่ของใครบางคน ความจริงใหม่) ...
Marjan Venema

1
ฉันจะไม่พูดว่าวิธีการคำกริยา / คำนามเป็น "ไร้สาระ" ผมบอกว่าการออกแบบที่เรียบง่ายจะล้มเหลวและว่ามันเป็นเรื่องยากมากที่จะได้รับสิทธิ เนื่องจากตัวอย่างแบบไม่ได้ RESTful API เป็นวิธีการ Verb / นาม ที่ไหนสำหรับระดับความยากพิเศษคำกริยามี จำกัด มาก?
user949300

@ user949300: ความจริงที่ว่าชุดของคำกริยาได้รับการแก้ไขหลีกเลี่ยงปัญหาที่ฉันกล่าวถึงอย่างแม่นยำนั่นคือคุณสามารถกำหนดประโยคในรูปแบบที่แตกต่างกันหลายวิธีจึงทำให้คำนามและคำกริยาขึ้นอยู่กับสไตล์การเขียนและไม่ใช่การวิเคราะห์โดเมน .
Jörg W Mittag

8

ทั้งหมดบ่อยเกินไปเดี่ยวรับผิดชอบหลักการจะกลายเป็นศูนย์ความรับผิดชอบต่อหลักการและคุณจะจบลงด้วยการเรียนโลหิตจางที่ไม่ทำอะไรเลย (ยกเว้น setters และ getters) ซึ่งจะนำไปสู่อาณาจักรของภัยพิบัติคำนาม

คุณจะไม่ได้รับมันสมบูรณ์แบบ แต่ดีกว่าสำหรับชั้นเรียนที่จะทำมากเกินไปน้อยเกินไป

ในตัวอย่างของคุณด้วยการเข้ารหัส IMO แน่นอนว่ามันควรจะสามารถเข้ารหัสได้ คุณควรทำสิ่งใดแทน เพียงแค่มีชื่อ "utf" นั้นไม่มีความรับผิดชอบ ตอนนี้บางทีชื่อควรจะเป็น Encoder แต่เป็นคอนราดกล่าวว่าข้อมูล (เข้ารหัส) และพฤติกรรม (ทำมัน) เป็นของร่วมกัน


7

ตัวอย่างของการเรียนคือการปิด แค่นั้นแหละ. หากคุณคิดว่าวิธีการที่ซอฟต์แวร์ที่ออกแบบมาอย่างดีทั้งหมดที่คุณมองนั้นจะดูถูกต้องและซอฟต์แวร์ที่ไม่ได้คุณภาพจะไม่คิด ให้ฉันขยาย

หากคุณต้องการเขียนบางสิ่งเพื่อเขียนไปยังไฟล์ให้คิดถึงทุกสิ่งที่คุณต้องการระบุ (ไปยัง OS): ชื่อไฟล์, วิธีการเข้าถึง (อ่าน, เขียน, ผนวก), สตริงที่คุณต้องการเขียน

ดังนั้นคุณสร้างวัตถุไฟล์ด้วยชื่อไฟล์ (และวิธีการเข้าถึง) ตอนนี้วัตถุไฟล์ถูกปิดทับชื่อไฟล์ (ในกรณีนี้มันอาจจะถูกใช้เป็นค่าแบบอ่านอย่างเดียว / const) ตอนนี้อินสแตนซ์ไฟล์พร้อมที่จะทำการเรียกไปยังเมธอด "write" ที่กำหนดในคลาส นี่ใช้อาร์กิวเมนต์ของสตริง แต่ในเนื้อความของวิธีการเขียนการใช้งานนั้นยังมีการเข้าถึงชื่อไฟล์ (หรือตัวจัดการไฟล์ที่สร้างขึ้นจากมัน)

อินสแตนซ์ของคลาสไฟล์จึงบรรจุข้อมูลลงในคอมโพสิตหยดบางชนิดเพื่อให้การใช้อินสแตนซ์นั้นในภายหลังนั้นง่ายขึ้น เมื่อคุณมีวัตถุไฟล์คุณไม่จำเป็นต้องกังวลว่าชื่อไฟล์คืออะไรคุณกังวลเกี่ยวกับสตริงที่คุณใส่เป็นอาร์กิวเมนต์ในการโทรเพื่อเขียน หากต้องการย้ำอีกครั้ง - วัตถุ File จะสรุปข้อมูลทั้งหมดที่คุณไม่จำเป็นต้องรู้เกี่ยวกับตำแหน่งที่สตริงที่คุณต้องการเขียนกำลังดำเนินการ

ปัญหาส่วนใหญ่ที่คุณต้องการแก้ไขนั้นเป็นชั้น ๆ ด้วยวิธีนี้ - สิ่งที่สร้างขึ้นด้านหน้าสิ่งที่สร้างขึ้นในตอนเริ่มต้นของการวนซ้ำของกระบวนการวนรอบแต่ละครั้งจากนั้นสิ่งต่าง ๆ ที่ประกาศไว้ในสองครึ่งของสิ่งอื่น จุดเริ่มต้นของการวนย่อยบางอย่างจากนั้นสิ่งต่าง ๆ ที่ประกาศเป็นส่วนหนึ่งของอัลกอริทึมภายในลูปนั้น ฯลฯ เมื่อคุณเพิ่มการเรียกฟังก์ชันไปยังสแต็กมากขึ้นเรื่อย ๆ คุณกำลังทำโค้ดที่ละเอียดยิ่งขึ้น แต่ทุกครั้งที่คุณมี บรรจุข้อมูลในเลเยอร์ที่ต่ำกว่าลงในสแต็กเป็นสิ่งที่เป็นนามธรรมซึ่งทำให้เข้าถึงได้ง่าย ดูสิ่งที่คุณกำลังทำอยู่โดยใช้ "OO" เป็นกรอบการจัดการการปิดสำหรับวิธีการเขียนโปรแกรมการทำงาน

โซลูชันทางลัดสำหรับปัญหาบางอย่างเกี่ยวข้องกับสถานะที่ไม่แน่นอนของวัตถุซึ่งไม่สามารถใช้งานได้ - คุณกำลังจัดการการปิดจากด้านนอก คุณกำลังทำบางสิ่งในชั้นเรียนของคุณ "ทั่วโลก" อย่างน้อยก็อยู่ในขอบเขตด้านบน ทุกครั้งที่คุณเขียน setter พยายามหลีกเลี่ยงสิ่งเหล่านั้น ฉันจะเถียงว่านี่คือที่ที่คุณได้รับความรับผิดชอบในชั้นเรียนของคุณหรือชั้นเรียนอื่น ๆ ที่มันทำงานผิด แต่ไม่ต้องกังวลมากเกินไป - บางครั้งมันเป็นประโยชน์ในการกวนในสถานะที่ไม่แน่นอนในการแก้ปัญหาบางประเภทได้อย่างรวดเร็วโดยไม่ต้องมีความรู้ความเข้าใจมากเกินไป

ดังนั้นโดยสรุปเพื่อตอบคำถามของคุณ - ความรับผิดชอบที่แท้จริงของชั้นเรียนคืออะไร? การนำเสนอแพลตฟอร์มชนิดหนึ่งซึ่งมีพื้นฐานจากข้อมูลบางอย่างเพื่อให้ได้การดำเนินการที่มีรายละเอียดมากขึ้น มันจะสรุปข้อมูลที่เกี่ยวข้องในการดำเนินการครึ่งหนึ่งที่เปลี่ยนแปลงน้อยกว่าอีกครึ่งหนึ่งของข้อมูล (คิดว่าเป็นการปิดบัง ... ) เช่นชื่อไฟล์และสตริงที่จะเขียน

มักจะห่อหุ้มเหล่านี้ดูเหมือนเผินๆเหมือนวัตถุในโลกแห่งความจริง / วัตถุในโดเมนปัญหา แต่ไม่หลงกล เมื่อคุณสร้างคลาสรถยนต์จริง ๆ แล้วมันไม่ใช่รถ แต่เป็นชุดข้อมูลที่รวบรวมแพลตฟอร์มเพื่อให้ได้สิ่งต่าง ๆ ที่คุณอาจต้องการพิจารณาทำในรถยนต์ การขึ้นรูปเป็นตัวแทนสตริง (toString) เป็นหนึ่งในสิ่งเหล่านั้น - กระจายข้อมูลภายในทั้งหมดนั้นออกไป โปรดจำไว้ว่าบางครั้งคลาสรถยนต์อาจไม่ใช่คลาสที่เหมาะสมแม้ว่าโดเมนปัญหาจะเกี่ยวกับรถยนต์ก็ตาม ตรงกันข้ามกับคำนามในราชอาณาจักรมันคือการดำเนินการคำกริยาที่ชั้นเรียนควรเป็นพื้นฐาน


4

ฉันเข้าใจด้วยว่าอาจไม่มีคำตอบที่แน่นอน (ซึ่งจะเป็นบทความมากกว่าคำตอบที่จริงจัง) สำหรับเรื่องนี้หรืออาจเป็นไปตามความคิดเห็น อย่างไรก็ตามฉันยังต้องการทราบว่าแนวทางของฉันเป็นไปตามหลักการความรับผิดชอบเดี่ยวหรือไม่

มันอาจไม่จำเป็นต้องสร้างรหัสที่ดี นอกเหนือจากความจริงง่ายๆที่กฎที่ติดตามแบบสุ่มสี่สุ่มห้านำไปสู่โค้ดที่ไม่ดีคุณกำลังเริ่มต้นหลักฐานที่ไม่ถูกต้อง: วัตถุ (การเขียนโปรแกรม) ไม่ใช่วัตถุ (ทางกายภาพ)

วัตถุคือชุดของการรวมข้อมูลและการดำเนินงานที่เหนียวแน่น (และบางครั้งมีเพียงหนึ่งในสอง) ในขณะที่มักจะรูปแบบวัตถุโลกแห่งความจริงเหล่านี้แตกต่างระหว่างแบบจำลองคอมพิวเตอร์ของบางสิ่งบางอย่างและสิ่งที่ตัวเองมีความจำเป็นที่แตกต่างกัน

โดยการเอาเส้นแบ่งระหว่าง "คำนาม" ที่แสดงถึงสิ่งของและสิ่งอื่น ๆ ที่กินเข้าไปคุณจะได้รับผลประโยชน์ที่สำคัญของการเขียนโปรแกรมเชิงวัตถุ (มีฟังก์ชั่นและสถานะร่วมกัน . ยิ่งกว่านั้นคุณกำลังพยายามที่จะเป็นตัวแทนของโลกทางกายภาพในรหัสซึ่งตามที่ได้แสดงให้เห็นประวัติศาสตร์จะไม่ทำงานได้ดี


4

ฉันเจอรหัส C # นี้ (ภาษาอยุ่, ภาษาวัตถุที่สี่ที่ใช้ในโพสต์นี้) เมื่อวันก่อน:

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

และฉันต้องบอกว่ามันไม่สมเหตุสมผลกับฉันมากนักว่า คลาสEncodingหรือCharsetคลาสจะเข้ารหัสสตริง มันควรจะเป็นตัวแทนของสิ่งที่เข้ารหัสจริงๆ

ในทางทฤษฎีแล้วใช่ แต่ฉันคิดว่า C # ทำให้การประนีประนอมมีเหตุผลเพื่อความเรียบง่าย

ถ้าEncodingเป็นเพียงการแทน (หรือระบุ) การเข้ารหัสบางอย่าง - พูด UTF-8 - คุณต้องมีEncoderคลาสด้วยเช่นกันเพื่อที่คุณจะสามารถนำไปใช้GetBytesกับมันได้ - แต่คุณต้องจัดการความสัมพันธ์ระหว่างEncoders และEncodings ดังนั้นเราจึง ท้ายที่สุดด้วย ol ดีของเรา

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

เหน็บแนมดังนั้นเก่งในบทความที่คุณเชื่อมโยงกับ (อ่านดีโดยวิธี)

ถ้าฉันเข้าใจคุณดีคุณกำลังถามว่าสายอยู่ที่ไหน

ฝั่งตรงข้ามของสเปกตรัมเป็นวิธีการดำเนินการไม่ยากที่จะนำมาใช้ในภาษา OOP ด้วยการใช้คลาสคงที่:

HelpfulStuff.GetUTF8Bytes(myString)

ปัญหาใหญ่เกี่ยวกับเรื่องนี้คือเมื่อผู้เขียนHelpfulStuffออกจากและฉันนั่งอยู่หน้าจอมอนิเตอร์ฉันไม่มีทางรู้ว่าGetUTF8Bytesจะถูกนำไปใช้ที่ไหน

ฉันจะมองหามันในHelpfulStuff, Utils, StringHelper?

ท่านช่วยฉันหน่อยถ้าวิธีนี้ถูกนำไปใช้อย่างอิสระในทั้งสามข้อนั้น ... ซึ่งเกิดขึ้นมากมายสิ่งที่ต้องทำก็คือผู้ชายคนก่อนหน้าฉันไม่รู้ว่าจะหาฟังก์ชั่นนั้นที่ไหนและเชื่อว่าไม่มีอะไรเลย แต่พวกเขาก็หักอีกคนหนึ่งและตอนนี้เรามีพวกมันมากมาย

สำหรับฉันการออกแบบที่เหมาะสมไม่ได้เกี่ยวกับเกณฑ์นามธรรมที่น่าพอใจ แต่เกี่ยวกับความสะดวกในการพกพาของฉันหลังจากที่ฉันนั่งอยู่หน้าโค้ดของคุณ ตอนนี้อย่างไร

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

ให้คะแนนในด้านนี้หรือไม่? ไม่ดีเช่นกัน ไม่ใช่คำตอบที่ฉันต้องการได้ยินเมื่อฉันส่งเสียงไปยังเพื่อนร่วมงานของฉัน "ฉันจะเข้ารหัสสตริงเป็นลำดับไบต์ได้อย่างไร" :)

ดังนั้นฉันจะไปด้วยวิธีปานกลางและมีอิสระเกี่ยวกับความรับผิดชอบเดียว ฉันควรให้Encodingคลาสทั้งสองระบุประเภทของการเข้ารหัสและทำการเข้ารหัสจริง: ข้อมูลไม่แยกจากพฤติกรรม

ฉันคิดว่ามันผ่านรูปแบบซุ้มซึ่งช่วยให้จารบีเกียร์ รูปแบบที่ถูกกฎหมายนี้ละเมิด SOLID หรือไม่ คำถามที่เกี่ยวข้อง: รูปแบบ Facade ละเมิด SRP หรือไม่

โปรดทราบว่านี่อาจเป็นวิธีการนำไบต์ที่เข้ารหัสไปใช้ภายใน (ในไลบรารี. NET) ชั้นเรียนไม่ปรากฏต่อสาธารณะและมีเพียง "facadeous" API นี้ที่เปิดเผยภายนอก มันไม่ยากเลยที่จะตรวจสอบว่ามันเป็นเรื่องจริงหรือเปล่าฉันแค่ขี้เกียจเกินไปที่จะทำตอนนี้ :) มันอาจจะไม่ใช่ แต่นั่นก็ไม่ได้ทำให้ประเด็นของฉันเสีย

คุณอาจเลือกที่จะทำให้การใช้งานที่เปิดเผยต่อสาธารณะง่ายขึ้นเพื่อประโยชน์ของความเป็นมิตรในขณะที่ยังคงมีการใช้งานที่รุนแรงและเป็นที่ยอมรับมากขึ้นภายใน


1

ใน Java สิ่งที่เป็นนามธรรมที่ใช้เพื่อแสดงไฟล์เป็นสตรีมสตรีมบางรายการเป็นทิศทางเดียว (อ่านอย่างเดียวหรือเขียนเท่านั้น) ส่วนอื่นเป็นแบบสองทิศทาง สำหรับ Ruby คลาส File เป็นสิ่งที่เป็นนามธรรมที่แทนไฟล์ OS ดังนั้นคำถามจะกลายเป็นความรับผิดชอบเดียวของมันคืออะไร? ใน Java ความรับผิดชอบของ FileWriter คือการจัดเตรียมสตรีมไบต์แบบทิศทางเดียวที่เชื่อมต่อกับไฟล์ OS ใน Ruby ความรับผิดชอบของไฟล์คือการให้การเข้าถึงแบบสองทิศทางไปยังไฟล์ระบบ พวกเขาทั้งสองปฏิบัติตาม SRP ที่พวกเขามีเพียงความรับผิดชอบที่แตกต่างกัน

มันไม่ใช่ความรับผิดชอบของ Car หรือ Dog ในการแปลงตัวมันเองให้เป็นสตริงตอนนี้ใช่ไหม?

แน่นอนว่าทำไมไม่ ฉันคาดว่าคลาสรถยนต์จะเป็นตัวแทนของสิ่งที่เป็นนามธรรม ถ้ามันสมเหตุสมผลสำหรับรถยนต์ที่เป็นนามธรรมที่จะใช้ในกรณีที่จำเป็นต้องใช้สตริงฉันคาดหวังอย่างเต็มที่ว่าคลาสรถยนต์จะรองรับการแปลงเป็นสตริง

เพื่อตอบคำถามที่ใหญ่กว่าโดยตรงความรับผิดชอบของชั้นเรียนคือการทำหน้าที่เป็นนามธรรม งานนั้นอาจซับซ้อน แต่นั่นเป็นประเด็นสำคัญสิ่งที่เป็นนามธรรมควรซ่อนสิ่งที่ซับซ้อนไว้ข้างหลังอินเตอร์เฟสที่ใช้งานง่ายกว่าและซับซ้อนน้อยกว่า SRP เป็นแนวทางการออกแบบดังนั้นคุณจึงมุ่งเน้นไปที่คำถาม "สิ่งที่เป็นนามธรรมนี้หมายถึงอะไร" หากจำเป็นต้องมีพฤติกรรมที่แตกต่างกันหลายอย่างเพื่อทำให้แนวคิดนั้นเป็นจริง


0

ชั้นสามารถมีความรับผิดชอบหลายอย่าง อย่างน้อยใน OOP ที่เน้นข้อมูลเป็นศูนย์กลาง

หากคุณใช้หลักการความรับผิดชอบเดี่ยวอย่างเต็มที่คุณจะได้รับการออกแบบที่เน้นความรับผิดชอบเป็นหลักซึ่งโดยทั่วไปแล้ววิธีการที่ไม่ใช่ตัวช่วยทั้งหมดเป็นของคลาสของตัวเอง

ฉันคิดว่ามีอะไรผิดปกติกับการแยกชิ้นส่วนของความซับซ้อนจากคลาสฐานเช่นในกรณีของ File และ FileWriter ข้อดีคือคุณจะได้รับการแยกรหัสอย่างชัดเจนซึ่งรับผิดชอบการดำเนินการบางอย่างและคุณสามารถแทนที่ (คลาสย่อย) เพียงแค่ส่วนของรหัสนั้นแทนการแทนที่คลาสฐานทั้งหมด (เช่นไฟล์) อย่างไรก็ตามการใช้ที่เป็นระบบดูเหมือนจะเป็น overkill ให้ฉัน คุณได้รับคลาสมากขึ้นเพื่อจัดการและสำหรับการดำเนินการทุกอย่างที่คุณต้องการเรียกใช้คลาสนั้น มันคือความยืดหยุ่นที่มาพร้อมกับราคา

เรียนสกัดเหล่านี้ควรมีชื่อที่สื่อความหมายมากขึ้นอยู่กับการดำเนินงานของพวกเขามีเช่น<X>Writer, ,<X>Reader <X>Builderชื่อเหมือน<X>Manager, <X>Handlerไม่ได้อธิบายการดำเนินการที่ทุกคนและถ้าพวกเขาจะเรียกเช่นนั้นเพราะพวกเขามีการดำเนินการมากกว่าหนึ่งแล้วมันจะไม่ชัดเจนสิ่งที่คุณได้ประสบความสำเร็จแม้กระทั่ง คุณได้แยกการทำงานจากข้อมูลของคุณยังคงทำลายหลักการรับผิดชอบเดียวแม้จะอยู่ในระดับที่คลายซิปและถ้าคุณกำลังมองหาวิธีการบางอย่างที่คุณอาจไม่ทราบว่าจะมองหามัน (ถ้าใน<X>หรือ<X>Manager)

ตอนนี้เคสของคุณที่มี UserDataHandler จะแตกต่างกันเนื่องจากไม่มีคลาสพื้นฐาน (คลาสที่มีข้อมูลจริง) ข้อมูลสำหรับคลาสนี้ถูกเก็บไว้ในทรัพยากรภายนอก (ฐานข้อมูล) และคุณกำลังใช้ API เพื่อเข้าถึงและจัดการพวกเขา คลาสผู้ใช้ของคุณแสดงถึงผู้ใช้ที่ใช้งานจริงซึ่งเป็นสิ่งที่แตกต่างจากผู้ใช้แบบถาวรและการแยกความรับผิดชอบ (ข้อกังวล) อาจมีประโยชน์ที่นี่โดยเฉพาะถ้าคุณมีตรรกะที่เกี่ยวข้องกับผู้ใช้จำนวนมาก

คุณอาจตั้งชื่อ UserDataHandler เช่นนั้นเนื่องจากมีเพียงฟังก์ชันการทำงานในคลาสนั้นและไม่มีข้อมูลจริง อย่างไรก็ตามคุณอาจเป็นนามธรรมจากข้อเท็จจริงนั้นและพิจารณาข้อมูลในฐานข้อมูลให้เป็นของชั้นเรียน ด้วยความที่เป็นนามธรรมนี้คุณสามารถตั้งชื่อชั้นขึ้นอยู่กับว่ามันคืออะไรและไม่ได้อยู่ในสิ่งที่มันไม่ คุณสามารถให้มันชื่อเหมือน UserRepository ซึ่งเป็นเนียนสวยและยังแสดงให้เห็นการใช้งานของรูปแบบการเก็บข้อมูล

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.