แยกคลาสออกจากส่วนติดต่อผู้ใช้


27

อะไรคือแนวปฏิบัติที่ดีที่สุดในการเขียนคลาสที่อาจต้องรู้เกี่ยวกับส่วนต่อประสานผู้ใช้ ชั้นเรียนจะรู้วิธีวาดตัวเองไม่ได้หรือไม่เพราะมันขึ้นอยู่กับว่าอินเทอร์เฟซผู้ใช้คืออะไร (คอนโซล, GUI, ฯลฯ )

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

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


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

2
"คลาสที่รู้วิธีวาดตัวเอง" ทั้งหมดเป็นเพียงตัวอย่างที่น่ากลัวและโบราณที่ฉันปรารถนาจะหายไป โดยเฉพาะอย่างยิ่งในกองโปรแกรมเมอร์ของเกม =)
Patrick Hughes

2
@Chad ฉันไม่ได้มีประสบการณ์มากนักนอกโรงเรียน ฉันอ่านหนังสือฉันชอบเรียนรู้และอ่านสิ่งใหม่ ๆ เกี่ยวกับรูปแบบการออกแบบและแนวปฏิบัติที่ดีที่สุด ใช่คุณสามารถพูดได้ว่าฉันได้ยินว่า decoupling นั้นดี แต่มันก็สมเหตุสมผล ฉันต้องการเขียนโค้ดที่ฉันสามารถใช้กับแอพ WinForms สำหรับเดสก์ท็อปแล้วใช้โค้ดนั้นและนำมาใช้ซ้ำให้มากที่สุดสำหรับเว็บไซต์หรือแม้แต่แอป Silverlight
Pete

@Pete - นั่นคือคำตอบที่ดี
SoylentGray

2
@Patrick: ถ้าคุณเขียนShapeคลาสคุณก็อาจจะเขียนกราฟิกสแต็กเองไม่ได้เขียนไคลเอ็นต์ไปยังกราฟิกสแต็ก
Ken Bloom

คำตอบ:


13

อะไรคือแนวปฏิบัติที่ดีที่สุดในการเขียนคลาสที่อาจต้องรู้เกี่ยวกับส่วนต่อประสานผู้ใช้ ชั้นเรียนจะรู้วิธีวาดตัวเองไม่ได้หรือไม่เพราะมันขึ้นอยู่กับว่าอินเทอร์เฟซผู้ใช้คืออะไร (คอนโซล, GUI, ฯลฯ )

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

ในหนังสือเขียนโปรแกรมหลายเล่มฉันเจอตัวอย่าง "รูปร่าง" ที่แสดงการสืบทอด รูปร่างคลาสพื้นฐานมีเมธอด draw () ที่แต่ละรูปร่างเช่นวงกลมและการแทนที่สแควร์ สิ่งนี้ทำให้เกิด polymorphism แต่วิธีการดึง () ไม่ใช่วิธีที่ขึ้นอยู่กับว่าส่วนต่อประสานผู้ใช้คืออะไร

อีกครั้งไม่จำเป็นต้อง หากคุณสามารถสร้างอินเทอร์เฟซ (drawPoint, drawLine, Set Color ฯลฯ ) คุณสามารถส่งผ่านบริบทใด ๆ สำหรับการวาดสิ่งลงบนสิ่งที่รูปร่างเช่นในตัวสร้างของรูปร่าง สิ่งนี้จะทำให้รูปร่างสามารถวาดตัวเองบนคอนโซลหรือพื้นที่วาดภาพใด ๆ

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

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

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

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

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

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

แก้ไข:หากคุณต้องการอ่านเพิ่มเติมเกี่ยวกับสถาปัตยกรรมและเทคนิคการออกแบบที่สามารถให้แนวทางปฏิบัติที่ดีที่สุดฉันขอแนะนำให้คุณอ่าน @Catchops คำตอบและอ่านเกี่ยวกับแนวทางปฏิบัติของSOLIDในวิกิพีเดีย

นอกจากนี้สำหรับ starters, ฉันมักจะแนะนำหนังสือเล่มต่อไปนี้: รูปแบบการออกแบบหัวแรก มันจะช่วยให้คุณเข้าใจเทคนิคที่เป็นนามธรรม / แนวปฏิบัติในการออกแบบ OOP มากกว่าหนังสือ GoF (ซึ่งยอดเยี่ยมมันไม่เหมาะกับผู้เริ่มต้น)


1
คำตอบที่ดี แต่โปรแกรมเมอร์สุดขีดไม่ได้วางในสิ่งที่เราคาดหวังว่าจะมีการเปลี่ยนแปลง เราใส่ในแนวคิดสิ่งที่มีการเปลี่ยนแปลงแห้งรหัส
วินไคลน์

1
@kevin cline นั้นยอดเยี่ยมเว้นแต่คุณจะออกแบบไลบรารีที่เปิดเผยต่อสาธารณะด้วย API ที่ต้องสอดคล้องกับพฤติกรรมและอินเทอร์เฟซก่อนหน้า
Magnus Wolffelt

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

19

คุณพูดถูก และไม่คุณกำลัง "พยายามได้ดี" :)

อ่านเกี่ยวกับหลักการความรับผิดชอบเดี่ยว

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

อย่ากลัวที่จะแยกชั้นเรียน ไม่ค่อยมีปัญหาที่เป็นนามธรรมมากเกินไปและ decoupling :)

สองรูปแบบที่เกี่ยวข้องอย่างมากคือModel – view – controllerสำหรับเว็บแอปพลิเคชันและModel View ViewModelสำหรับ Silverlight / WPF


1
+1 คุณสามารถใช้ MVC สำหรับแอปพลิเคชันที่ไม่ใช่เว็บได้อย่างน้อยที่สุดมันช่วยให้คุณคิดในแง่ที่ทำให้ความรับผิดชอบชัดเจน
Patrick Hughes

MVVM เป็น silimar ถึง MVC MVC ส่วนใหญ่สำหรับแอปพลิเคชันไร้สัญชาติเช่นเว็บแอป
Boris Yankov

3
-1 ในประสบการณ์ของฉันหนึ่งในปัญหาที่พบบ่อยที่สุดคือการวางนัยทั่วไปก่อนวัยและโดยทั่วไปทางวิศวกรรม
dietbuddha

3
ก่อนอื่นคุณต้องรู้วิธีการสร้างบางสิ่งบางอย่างเพื่อที่จะสร้างมันขึ้นมา จากประสบการณ์ของฉันซอฟต์แวร์บ่อยครั้งกว่า
Boris Yankov

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

7

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

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

ดังนั้นฉันจะบอกว่าอย่าหยุดพยายามแยก UI และคลาสของคุณออกจากกัน ยิ่งพวกเขาแยกชิ้นส่วนออกได้มากเท่าไรก็ยิ่งง่ายต่อการบำรุงรักษาและทดสอบ


5

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

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

นั่นคือสิ่งที่อินเทอร์เฟซเข้ามา - เนื่องจากสถานะของ Gang of Four book (ต้องอ่าน) - ควรใช้การสืบทอดมากกว่ามรดกเสมอ กล่าวอีกนัยหนึ่งให้ใช้อินเทอร์เฟซเพื่อเชื่อมต่อสถาปัตยกรรมที่สามารถใช้งานฟังก์ชั่นต่าง ๆ ได้หลายวิธีโดยไม่ต้องอาศัยการอ้างอิงแบบฮาร์ดโค้ด

ฉันเห็นผู้ตัดสินตามหลักการของSOLIDแล้ว มันเยี่ยมมาก 'S' เป็นหลักการความรับผิดชอบเดียว แต่ 'D' ย่อมาจาก Dependency Inversion รูปแบบการผกผันของการควบคุม (การพึ่งพาการฉีด) สามารถใช้ได้ที่นี่ มันมีประสิทธิภาพมากและสามารถใช้ในการตอบคำถามของวิธีการออกแบบระบบที่สามารถวาดวงกลมสำหรับ iPhone เช่นเดียวกับพีซี

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

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

โชคดี!


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

2

อะไรคือแนวปฏิบัติที่ดีที่สุดในการเขียนคลาสที่อาจต้องรู้เกี่ยวกับส่วนต่อประสานผู้ใช้

ชั้นเรียนจะรู้วิธีวาดตัวเองไม่ได้หรือไม่เพราะมันขึ้นอยู่กับว่าอินเทอร์เฟซผู้ใช้คืออะไร (คอนโซล, GUI, ฯลฯ )

ในกรณีนี้คุณยังสามารถใช้ MVC / MVVM และฉีดการใช้งาน UI ที่แตกต่างกันโดยใช้อินเทอร์เฟซทั่วไป:

public interface IAgnosticChartDrawing
{
   public void Draw(ChartProperties chartProperties);
   event EventHandler ChartPanned;
}

public class GuiChartDrawer : UserControl, IAgnosticChartDrawing
{
    public void Draw(ChartProperties chartProperties)
    {
        //GDI, GTK or something else...
    }

    //Implement event based on mouse actions
}

public class ConsoleChartDrawer : IAgnosticChartDrawing
{
    public void Draw(ChartProperties chartProperties)
    {
        //'Draw' using characters and symbols...
    }

    //Implement event based on keyboard actions
}

IAgnosticChartDrawing guiView = new GuiChartDrawer();
IAgnosticChartDrawing conView = new ConsoleChartDrawer();

Model model = new FinancialModel();

SampleController controllerGUI = new SampleController(model, guiView);
SampleController controllerConsole = new SampleController(model, conView);

วิธีนี้คุณจะสามารถใช้คอนโทรลเลอร์และโมเดลของคุณได้อีกครั้งในขณะที่สามารถเพิ่ม GUI ชนิดใหม่ได้


2

มีรูปแบบที่แตกต่างกันดังนี้: MVP, MVC, MVVM, ฯลฯ ...

บทความที่ดีจาก Martin Fowler ในการอ่านคือชื่อสถาปัตยกรรม GUI: http://www.martinfowler.com/eaaDev/uiArchs.html

MVP ยังไม่ได้รับการกล่าวถึง แต่แน่นอนสมควรได้รับการกล่าวถึง: ดูที่มัน

เป็นรูปแบบที่นักพัฒนาซอฟต์แวร์ Google Web Toolkit แนะนำให้ใช้มันเป็นระเบียบจริงๆ

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

http://code.google.com/webtoolkit/articles/mvp-architecture.html

http://code.google.com/webtoolkit/articles/mvp-architecture-2.html

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


1
+1 สำหรับลิงก์ ฉันอ่านบทความที่คล้ายกันใน MSDN เกี่ยวกับ MVVM และบทความ google เหล่านั้นดีกว่ามากแม้ว่าในรูปแบบที่แตกต่างกันเล็กน้อย
Pete

1

นี่เป็นหนึ่งในสถานที่ที่ OOP ไม่สามารถทำงานได้ดี ความหลากหลาย OOP ใช้การกระจายแบบไดนามิกมากกว่าตัวแปรเดียว ('นี่') ถ้า polymorphism ถูกรูทที่ Shape คุณจะไม่สามารถส่ง polymorphic-ally บน renderer (console, GUI และอื่น ๆ )

พิจารณาระบบการเขียนโปรแกรมที่สามารถจัดส่งตัวแปรสองตัวหรือมากกว่า:

poly_draw(Shape s, Renderer r)

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

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


1

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

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

... ฉันพบว่าตัวเองติดอยู่เสมอและวางตัวในการพูดคุยในชั้นเรียนทั่วไปเพื่อให้มีประโยชน์มากที่สุด

สิ่งที่ผมทำในปัจจุบันข้างต้นคือข้อต่อ View-Controller ที่เรากำลังพูดถึงนั้นโอเคและยิ่งกว่านั้นมันค่อนข้างทันสมัยในการออกแบบที่ทันสมัย

แม้ว่าหนึ่งหรือสองปีที่ผ่านมาฉันก็รู้สึกไม่ปลอดภัยเช่นกัน ฉันเปลี่ยนใจหลังจากศึกษาการอภิปรายที่ฟอรัม Sun เกี่ยวกับรูปแบบและการออกแบบ OO

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


1

แต่วิธีการดึง () ไม่ใช่วิธีที่ขึ้นอยู่กับว่าส่วนต่อประสานผู้ใช้คืออะไร

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

คำถามจากมุมมองของการมีเพศสัมพันธ์คือใคร / สิ่งที่ควรขึ้นอยู่กับข้อมูลประเภทนี้และระดับรายละเอียด (เช่นนามธรรมเช่น)?

ความสามารถในการวาด / การแสดงบทคัดย่อ

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

การพิจารณาก็เป็นวิธีการที่ง่ายก็คือการออกแบบที่เป็นนามธรรมที่มีเสถียรภาพหรือไม่? เพราะอาจเป็นเรื่องง่ายหากคุณกำหนดเป้าหมายทั้งหมดเป็น GUI APIs ที่ทันสมัยเพื่อย่อความสามารถในการวาดรูปร่างพื้นฐานเช่นการพล็อตเส้น, สี่เหลี่ยม, พา ธ , ข้อความ, สิ่งต่าง ๆ แบบนี้ (เพียงแค่การแรสเตอร์ 2D แบบง่ายๆ ด้วยอินเทอร์เฟซแบบนามธรรมเดียวที่สามารถนำไปใช้งานได้อย่างง่ายดายผ่านประเภทย่อยต่างๆด้วยค่าใช้จ่ายน้อย หากคุณสามารถออกแบบสิ่งที่เป็นนามธรรมได้อย่างมีประสิทธิภาพและนำไปใช้กับแพลตฟอร์มเป้าหมายทั้งหมดฉันจะบอกว่ามันเป็นความชั่วร้ายที่น้อยกว่ามากถ้าแม้แต่ความชั่วร้ายเลยสำหรับรูปร่างหรือการควบคุม GUI หรืออะไรก็ตามที่รู้วิธีวาดตัวเองโดยใช้ สิ่งที่เป็นนามธรรม

แต่สมมติว่าคุณกำลังพยายามที่จะสรุปรายละเอียดเลือดออกที่แตกต่างกันระหว่าง Playstation Portable, iPhone, XBox One และพีซีเกมที่ทรงพลังในขณะที่ความต้องการของคุณคือการใช้เทคนิคการเรนเดอร์ / การแรเงา 3D แบบเรียลไทม์ . ในกรณีนั้นพยายามที่จะสร้างอินเทอร์เฟซแบบนามธรรมเพื่อสรุปรายละเอียดการแสดงผลเมื่อความสามารถของฮาร์ดแวร์และ API แตกต่างกันอย่างมากจนแทบจะมั่นใจได้ว่าจะส่งผลให้มีเวลาในการออกแบบและการออกแบบใหม่อย่างมหาศาล การค้นพบและเช่นเดียวกันกับโซลูชันตัวหารร่วมที่ต่ำที่สุดที่ไม่สามารถใช้ประโยชน์จากความเป็นเอกลักษณ์และพลังของฮาร์ดแวร์พื้นฐานได้

ทำให้การอ้างอิงไหลต่อการออกแบบที่ "ง่าย"

ในสาขาของฉันฉันอยู่ในสถานการณ์สุดท้าย เรากำหนดเป้าหมายฮาร์ดแวร์จำนวนมากที่มีขีดความสามารถและ API ที่แตกต่างกันอย่างรุนแรงและพยายามสร้างการวาดภาพ / การวาดภาพหนึ่งสิ่งที่เป็นนามธรรมเพื่อปกครองพวกเขาทั้งหมดนั้นไร้ขอบเขต (เราอาจกลายเป็นคนดังระดับโลก ตัวเปลี่ยนในอุตสาหกรรม) ดังนั้นสิ่งสุดท้ายที่ฉันต้องการในกรณีของฉันเป็นเหมือนกระเชอShapeหรือModelหรือParticle Emitterที่รู้วิธีการวาดตัวเองแม้ว่าจะมีการแสดงที่วาดภาพในระดับสูงสุดและวิธีการที่เป็นนามธรรมมากที่สุด ...

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

ยากขึ้นอยู่กับง่ายไม่ง่ายขึ้นอยู่กับยาก

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

ป้อนคำอธิบายรูปภาพที่นี่

ดังนั้นเราจึงกลับการพึ่งพาจากสิ่งที่ยากต่อการออกแบบ แทนที่จะทำให้แบบจำลองนามธรรมรู้วิธีวาดตัวเองไปสู่การออกแบบ renderer นามธรรมที่พวกเขาทั้งหมดขึ้นอยู่กับ (และทำลายในการใช้งานของพวกเขาหากการเปลี่ยนแปลงการออกแบบที่) เราแทนที่จะมี renderer นามธรรมที่รู้วิธีการวาดวัตถุนามธรรมทุกฉากในฉากของเรา นางแบบ, ตัวปล่อยอนุภาค, ฯลฯ ), และดังนั้นเราจึงสามารถใช้ OpenGL renderer subtype สำหรับพีซีเช่นRendererGl, อีกอันสำหรับ PSPs RendererPsp, อีกอันสำหรับโทรศัพท์มือถือ, ฯลฯ ในกรณีนั้นการพึ่งพานั้นไหลไปสู่การออกแบบที่เสถียร, ง่ายต่อการแก้ไข, ตั้งแต่ตัวเรนเดอร์ไปจนถึงเอนทิตีประเภทต่าง ๆ (โมเดลอนุภาคพื้นผิว ฯลฯ ) ในฉากของเราไม่ใช่วิธีอื่น

ป้อนคำอธิบายรูปภาพที่นี่

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

หากคุณพบว่าตัวเองพยายามที่จะแยกแยะสิ่งที่อยู่ในระดับกลางของ codebase ของคุณและมันก็ยากเกินกว่าที่จะออกแบบแทนที่จะตีหัวกับกำแพงและหัวรุนแรงเปลี่ยนแปลงอย่างต่อเนื่องในแต่ละเดือน / ปีที่ต้องมีการอัพเดทไฟล์ต้นฉบับ 8,000 ไฟล์ ทำลายทุกอย่างที่ขึ้นอยู่กับมันข้อเสนอแนะหมายเลขหนึ่งของฉันคือการพิจารณาคว่ำการอ้างอิง ดูว่าคุณสามารถเขียนโค้ดในลักษณะที่สิ่งที่ยากในการออกแบบหรือไม่นั้นขึ้นอยู่กับทุกสิ่งที่ง่ายต่อการออกแบบไม่มีสิ่งที่ง่ายต่อการออกแบบขึ้นอยู่กับสิ่งที่ยากในการออกแบบ โปรดทราบว่าฉันกำลังพูดถึงการออกแบบ (โดยเฉพาะการออกแบบส่วนต่อประสาน) และไม่ใช้งาน: บางครั้งสิ่งที่ง่ายต่อการออกแบบและยากที่จะนำไปใช้ และบางครั้งสิ่งที่ยากในการออกแบบ แต่ใช้งานง่าย การไหลเวียนของการพึ่งพาสู่การออกแบบดังนั้นการโฟกัสควรอยู่ที่ความยากลำบากในการออกแบบที่นี่เพื่อกำหนดทิศทางการไหลของการพึ่งพา

หลักการความรับผิดชอบเดี่ยว

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

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

ความภาคภูมิใจของนักพัฒนา

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


1
ฉันดูเหมือนจะไม่เข้าใจความคิดของ "คว่ำการพึ่งพาห่างจากสิ่งที่ยากต่อการออกแบบ" คุณพูดถึงมรดกเท่านั้นหรือไม่ เมื่อใช้ตัวอย่างของ PSP / OpenGL คุณหมายถึงแทนที่จะสร้าง a OpenGLBillboardคุณจะต้องOpenGLRendererรู้ว่าจะวาดรูปแบบใดได้IBillBoardบ้าง แต่มันจะทำเช่นนั้นโดยมอบหมายตรรกะให้IBillBoardหรือจะมีสวิทช์หรือIBillBoardชนิดที่มีเงื่อนไขเป็นจำนวนมากหรือไม่? นั่นคือสิ่งที่ฉันเข้าใจยากเพราะมันดูไม่บำรุงเลย!
Steve Chamaillard

@SteveChamaillard ความแตกต่างคือการพูด, PSP ( จำกัด ฮาร์ดแวร์) ต้องจัดการวิทยาการปริมาณการใช้เก่าโปร่งใสโรงเรียน screendoor และการสั่งซื้อที่แตกต่างกันของการประเมินผลการแสดงผล: digitalrune.github.io/DigitalRune-Documentation/media/... เมื่อคุณมีศูนย์กลางเพียงอันเดียวRendererPspซึ่งรู้ว่า abstractions ระดับสูงของฉากเกมของคุณมันสามารถทำเวทย์มนตร์และแบ็คลิปทั้งหมดที่มันต้องการเพื่อแสดงสิ่งต่าง ๆ ในแบบที่ดูน่าเชื่อถือบน PSP ....
Dragon Energy

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

1
โอเคฉันคิดว่าฉันได้รับแล้ว โดยพื้นฐานแล้วคุณกำลังบอกว่าข้อกังวล (เช่นฮาร์ดแวร์) นั้นอยู่ในระดับสูงจนทำให้วัตถุระดับต่ำเช่นการBillBoardพึ่งพาสิ่งเหล่านั้นยากที่จะทำจริงๆ? ในขณะที่IRendererระดับสูงอยู่แล้วและอาจขึ้นอยู่กับข้อกังวลเหล่านี้ด้วยปัญหาน้อยลงมาก?
Steve Chamaillard

1
ด้วยความสามารถในการเรนเดอร์ที่ทันสมัยเกี่ยวกับแพลตฟอร์มที่แตกต่างกัน, ความสามารถของฮาร์ดแวร์ที่แตกต่างกัน, มันยากเกินไปที่จะออกแบบบางสิ่งที่ฉันเป็นหัวหน้าและบอกสิ่งที่ต้องทำ มันง่ายกว่ามากสำหรับฉันที่จะพูดว่า "เฮ้ฉันเป็นคนที่มีเมฆมาก / มีหมอกมีอนุภาคน้ำสีเทาแบบนี้และพยายามทำให้ฉันดูดีได้โปรดอย่าจับคอของฉันซึ่งฉันไม่ได้โกนเมื่อเร็ว ๆ นี้" และให้ผู้แสดงภาพหาวิธีทำให้ฉันดูสวยงามและเป็นแบบเรียลไทม์ให้มากที่สุดเท่าที่จะเป็นไปได้เนื่องจากข้อ จำกัด ที่ใช้กับมัน ความพยายามใด ๆ ที่จะบอกว่าสิ่งที่ต้องทำจะส่งผลให้เกิดเส้นทางการต่อต้าน
Dragon Energy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.