ถือว่าเป็นอันตรายหรือไม่? รหัสสามารถทำงานได้โดยไม่ได้หรือไม่


45

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

ฉันสงสัยว่ารหัสที่เขียนได้ดีสามารถปฏิบัติตามหลักการ OO และหลักการทำงานได้ในเวลาเดียวกัน returnฉันพยายามที่จะกระทบความคิดเหล่านี้และจุดเกาะใหญ่ที่ผมเคยมีที่ดินบน

ฟังก์ชั่นบริสุทธิ์มีสองคุณสมบัติ:

  1. การเรียกซ้ำ ๆ ด้วยอินพุตเดียวกันจะให้ผลลัพธ์เดียวกันเสมอ นี่ก็หมายความว่ามันไม่เปลี่ยนรูป สถานะของมันถูกตั้งค่าเพียงครั้งเดียว

  2. มันไม่มีผลข้างเคียง การเปลี่ยนแปลงเพียงอย่างเดียวที่เกิดจากการเรียกมันคือการสร้างผลลัพธ์

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

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

ฉันคิดว่า Tell, Don't Ask เป็นมากกว่าชื่อที่แตกต่างกันสำหรับการห่อหุ้ม เมื่อฉันใช้returnฉันไม่มีความคิดในสิ่งที่เรียกว่าฉัน ฉันไม่สามารถพูดได้ว่าเป็นโปรโตคอลฉันต้องบังคับให้จัดการกับโปรโตคอลของฉัน ซึ่งในหลายกรณีได้รับการแสดงออกเป็นสถานะภายใน แม้ว่าสิ่งที่ได้รับการเปิดเผยไม่ได้ระบุอย่างชัดเจน แต่โดยทั่วไปแล้วการคำนวณบางอย่างที่ทำกับสถานะและอินพุต args มีอินเทอร์เฟซสำหรับการตอบสนองผ่านกำบังโอกาสที่จะนวดผลลัพธ์เป็นสิ่งที่มีความหมายมากกว่าสถานะภายในหรือการคำนวณ นั่นคือข้อความผ่าน ดูตัวอย่างนี้

ย้อนกลับไปในวันที่เมื่อดิสก์ไดรฟ์มีดิสก์อยู่ในนั้นและไดรฟ์หัวแม่มือคือสิ่งที่คุณทำในรถเมื่อล้อเย็นเกินไปที่จะสัมผัสด้วยนิ้วมือของคุณฉันได้รับการสอนว่าคนที่น่ารำคาญพิจารณาฟังก์ชั่น void swap(int *first, int *second)ดูเหมือนมีประโยชน์มาก แต่เราได้รับการสนับสนุนให้เขียนฟังก์ชันที่ส่งคืนผลลัพธ์ ดังนั้นฉันจึงใส่ใจเรื่องความเชื่อและเริ่มติดตามมัน

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

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

สิ่งที่ฉันพยายามถามจริงๆ:

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


3
หากคุณเลือกที่จะไม่เพิกเฉยต่อการแยกคำสั่งคุณจะพิจารณาว่าปัญหาของคุณได้รับการแก้ไขหรือไม่?

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

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

16
@james large: ขั้วต่อเท็จ การไม่ยอมให้ตัวคุณถูกขับเคลื่อนด้วยการออกแบบโดยการคิดดื้อดึงนั้นไม่เหมือนกับ Wild West / ทุกอย่างเป็นไปตามที่คุณกำลังพูดถึง ประเด็นก็คือไม่อนุญาตให้มีความเชื่อในทางที่ดีเรียบง่ายชัดเจนรหัส กฎหมายสากลสำหรับการขาด; บริบทสำหรับพระมหากษัตริย์
Nicol Bolas

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

คำตอบ:


96

หากฟังก์ชั่นไม่มีผลข้างเคียงใด ๆ และไม่ส่งคืนสิ่งใดฟังก์ชันนั้นก็จะไร้ประโยชน์ มันเป็นเรื่องง่ายเหมือนที่.

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

คุณสามารถใช้กลโกงที่ซับซ้อนมากขึ้น เช่น Haskell มีชื่อเสียงในเรื่องการหลอกลวง IO monad ที่คุณสามารถมีผลข้างเคียงในทางปฏิบัติ แต่ก็ยังไม่พูดอย่างเคร่งครัดมีผลข้างเคียงจากมุมมองทางทฤษฎี รูปแบบการส่งต่อเป็นอีกเทคนิคหนึ่งที่ช่วยให้คุณหลีกเลี่ยงผลตอบแทนในราคาที่เปลี่ยนรหัสของคุณเป็นสปาเก็ตตี้

บรรทัดล่างคือกลอุบายที่ขาดไปสองหลักการของฟังก์ชั่นฟรีที่มีผลข้างเคียงและ "ไม่มีผลตอบแทน" นั้นเข้ากันไม่ได้ นอกจากนี้ฉันจะชี้ให้เห็นว่าทั้งคู่เป็นหลักการที่ไม่ดีจริงๆ (dogmas จริงๆ) ในตอนแรก แต่นั่นคือการอภิปรายที่แตกต่างกัน

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

กฎ tell-dont-ask ใช้กับสถาปัตยกรรมประเภทอื่น - สไตล์ที่วัตถุในโปรแกรมเป็นอิสระจาก "นักแสดง" ซึ่งสื่อสารกัน นักแสดงแต่ละคนนั้นเป็นอิสระและห่อหุ้ม คุณสามารถส่งข้อความและตัดสินใจว่าจะตอบโต้อย่างไร แต่คุณไม่สามารถตรวจสอบสถานะภายในของนักแสดงจากภายนอกได้ ซึ่งหมายความว่าคุณไม่สามารถบอกได้ว่าข้อความเปลี่ยนแปลงสถานะภายในของนักแสดง / วัตถุ รัฐและผลข้างเคียงจะถูกซ่อนไว้โดยการออกแบบ


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

12
@LightnessRacesinOrbit: grep ในโหมดเงียบมีโค้ดส่งคืนกระบวนการซึ่งจะถูกใช้ หากไม่มีสิ่งนั้นมันก็จะไร้ประโยชน์อย่างแท้จริง
unperson325680

10
@progo: โค้ดส่งคืนไม่มีผลข้างเคียง มันเป็นผล ทุกสิ่งที่เราเรียกว่าผลข้างเคียงเรียกว่าผลข้างเคียงเพราะไม่ใช่รหัสส่งคืน)
Lightness Races กับ Monica

8
@ ลูกบาศก์นั่นคือประเด็น โปรแกรมที่ไม่มีผลข้างเคียงนั้นไร้ประโยชน์
Jens Schauder

5
@mathreadler นั่นเป็นผลข้างเคียง :)
Andres F.

32

บอกอย่าถามมาพร้อมกับสมมติฐานพื้นฐาน:

  1. คุณกำลังใช้วัตถุ
  2. วัตถุของคุณมีสถานะ
  3. สถานะของวัตถุของคุณมีผลต่อพฤติกรรมของพวกเขา

ไม่มีสิ่งเหล่านี้ใช้กับฟังก์ชั่นบริสุทธิ์

ดังนั้นให้ตรวจสอบว่าทำไมเราถึงมีกฎ "บอกไม่ต้องถาม" กฎนี้เป็นการเตือนและเตือนความจำ สามารถสรุปได้ดังนี้:

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

หากต้องการกล่าวอีกวิธีหนึ่งชั้นเรียนมีหน้าที่รับผิดชอบ แต่เพียงผู้เดียวในการรักษาสภาพของตนเองและดำเนินการกับมัน นี่คือสิ่งที่ encapsulation เกี่ยวกับ

จากฟาวเลอร์ :

Tell-Don't-Askเป็นหลักการที่ช่วยให้ผู้คนจำได้ว่าการวางแนวของวัตถุนั้นเกี่ยวกับการรวมข้อมูลกับฟังก์ชันที่ทำงานกับข้อมูลนั้น มันเตือนเราว่าแทนที่จะขอวัตถุสำหรับข้อมูลและทำหน้าที่เกี่ยวกับข้อมูลนั้นเราควรบอกวัตถุว่าจะทำอย่างไร สิ่งนี้กระตุ้นให้เราย้ายพฤติกรรมไปยังวัตถุเพื่อไปกับข้อมูล

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

การละเมิด TDA

var color = trafficLight.Color;
var elapsed = trafficLight.Elapsed;
If (color == Color.Red && elapsed > 2.Minutes)
    trafficLight.ChangeColor(green);

ไม่ใช่การละเมิด TDA

var result = trafficLight.ChangeColor(Color.Green);

หรือ

var result = await trafficLight.ChangeColorWhenReady(Color.Green);     

ในทั้งสองตัวอย่างหลังสัญญาณไฟจราจรยังคงควบคุมสถานะและการกระทำของมัน


รอสักครู่การปิดสามารถบริสุทธิ์ได้ พวกเขามีสถานะ (พวกเขาเรียกว่าขอบเขตคำศัพท์) และขอบเขตศัพท์ที่สามารถส่งผลกระทบต่อพฤติกรรมของพวกเขา คุณแน่ใจหรือไม่ว่า TDA นั้นเกี่ยวข้องเฉพาะกับวัตถุที่เกี่ยวข้องเท่านั้น
candied_orange

7
@CandiedOrange closures เป็นจริงเท่านั้นหากคุณไม่ได้แก้ไขการผูกมัดแบบปิด มิฉะนั้นคุณจะสูญเสียความโปร่งใสในการอ้างอิงเมื่อเรียกใช้ฟังก์ชันที่ส่งคืนจากการปิด
Jared Smith

1
@ JaredSmith และไม่เหมือนกันเมื่อคุณพูดถึงวัตถุ? นี่ไม่ใช่ปัญหาที่ไม่สามารถเปลี่ยนแปลงได้หรือ
candied_orange

1
@bdsl - และตอนนี้คุณได้ชี้ให้เห็นอย่างชัดเจนว่าการอภิปรายประเภทนี้ทุกครั้งไปที่ตัวอย่างของ trafficLight.refreshDisplay หากคุณปฏิบัติตามกฎคุณจะนำไปสู่ระบบที่มีความยืดหยุ่นสูงซึ่งไม่มีใครนอกจาก coder ดั้งเดิมเข้าใจ ฉันจะพนันได้เลยว่าหลังจากผ่านไปสองปีที่หายไปแม้แต่ coder ดั้งเดิมก็คงไม่เข้าใจว่าพวกเขาทำอะไร
Dunk

1
"Silly" เช่นเดียวกับใน "อย่าเปิดวัตถุอื่น ๆ และไม่มองที่ความกล้าของพวกเขา แต่แทนที่จะทะลักความกล้าหาญของคุณเอง (ถ้าคุณมีใด ๆ ) ลงในวิธีการวัตถุอื่น ๆ ;
Joker_vD

30

เมื่อฉันจัดการกับวัตถุฉันไม่ถามเกี่ยวกับสถานะภายในของวัตถุนั้น ฉันบอกสิ่งที่ฉันต้องทำและใช้สถานะภายในเพื่อหาว่าจะทำอย่างไรกับสิ่งที่ฉันบอกให้ทำ

คุณไม่เพียง แต่ขอสถานะภายในแต่คุณไม่ต้องถามว่ามีสถานะภายในหรือไม่

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


1
ถึงแม้ว่า CQS จะบอกเป็นนัยว่าการปรับเปลี่ยนสถานะและผลลัพธ์ควรได้รับการแยก
jk

7
@jk ตามปกติ: โดยทั่วไปคุณควรแยกการเปลี่ยนแปลงสถานะและส่งคืนผลลัพธ์แต่ในบางกรณีมีเหตุผลที่ไม่ถูกต้องที่จะรวมเข้าด้วยกัน เช่น: iterators next()วิธีไม่ควรกลับวัตถุปัจจุบัน แต่ยังเปลี่ยน iterators รัฐภายในเพื่อให้โทรไปส่งกลับวัตถุต่อไป ...
ทิโมธียอมจำนน

4
เผง ฉันคิดว่าปัญหาของ OP นั้นเกิดจากความเข้าใจผิด / การใช้ผิด ๆ “ บอกอย่าถาม” และการแก้ไขความเข้าใจผิดที่ทำให้ปัญหาหายไป
Konrad Rudolph

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

17

หากคุณคิดreturnว่า "เป็นอันตราย" (อยู่ในรูปภาพของคุณ) แทนที่จะทำหน้าที่เช่นนั้น

ResultType f(InputType inputValue)
{
     // ...
     return result;
}

สร้างมันในลักษณะการส่งข้อความ:

void f(InputType inputValue, Action<ResultType> g)
{
     // ...
     g(result);
}

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

หากสิ่งนี้นำไปสู่โปรแกรมที่ "ดีกว่า" นั้นเป็นที่ถกเถียงกันอยู่ Ralf Westphal วิศวกรซอฟต์แวร์ชาวเยอรมันได้สร้างโมเดลการเขียนโปรแกรมทั้งหมดรอบนี้เขาเรียกมันว่า "Event Based Components" ด้วยเทคนิคการสร้างแบบจำลองที่เขาเรียกว่า "Flow Design"

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


24
If this really leads to "better" programs is debatableเราต้องดูโค้ดที่เขียนด้วย JavaScript ในช่วงทศวรรษแรกของศตวรรษนี้ Jquery และปลั๊กอินของมันมีแนวโน้มที่จะกระบวนทัศน์นี้เรียกกลับ ... เรียกกลับได้ทุกที่ เมื่อถึงจุดหนึ่งการเรียกกลับที่ซ้อนกันมากเกินไปทำให้การดีบักฝันร้าย มนุษย์ยังคงต้องอ่านรหัสโดยไม่คำนึงถึงความผิดปกติของวิศวกรรมซอฟต์แวร์และ "หลักการ" ของมัน
Laiv

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

12
@Laiv CPS ถูกประดิษฐ์ขึ้นเป็นเทคโนโลยีการเพิ่มประสิทธิภาพสำหรับคอมไพเลอร์ไม่มีใครคาดหวังว่าโปรแกรมเมอร์จะเขียนโค้ดด้วยวิธีนี้ด้วยมือ
Joker_vD

3
@CandiedOrange ในรูปแบบสโลแกน "การกลับมาเป็นเพียงการบอกต่อสิ่งที่ต้องทำ" อันที่จริงการสร้าง Scheme นั้นได้แรงบันดาลใจจากการพยายามที่จะเข้าใจรูปแบบนักแสดงของ Hewitt และได้ข้อสรุปว่านักแสดงและการปิดเป็นเรื่องเดียวกัน
Derek Elkins

2
สมมุติฐานแน่ใจว่าคุณสามารถเขียนโปรแกรมทั้งหมดของคุณเป็นชุดของสายฟังก์ชั่นที่ไม่ได้อะไรกลับมา หากคุณไม่คิดว่ามันถูกผนวกเข้าด้วยกันอย่างแน่นหนาคุณไม่จำเป็นต้องใช้พอร์ต แต่แอปพลิเคชั่นที่มีสติส่วนใหญ่ใช้ฟังก์ชั่นที่คืนสิ่งต่าง ๆ เพราะ ... เพราะมันมีสติอยู่ และในขณะที่ฉันเชื่อว่าฉันแสดงให้เห็นอย่างเพียงพอในคำตอบของฉันคุณสามารถreturnข้อมูลจากฟังก์ชั่นและยังคงยึดมั่นในการบอกอย่าถามตราบใดที่คุณไม่ได้ควบคุมสถานะของวัตถุ
Robert Harvey

7

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

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

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


5

คำถามทั้งหมดนี้ทำให้ฉันเป็น 'การละเมิดระดับ'

คุณมีระดับดังต่อไปนี้ในโครงการสำคัญ: (อย่างน้อย)

  • ระดับระบบเช่นแพลตฟอร์มอีคอมเมิร์ซ
  • ระดับระบบย่อยเช่นการตรวจสอบผู้ใช้: เซิร์ฟเวอร์, AD, Front-end
  • ระดับโปรแกรมแต่ละตัวเช่นหนึ่งในองค์ประกอบในข้างต้น
  • ระดับนักแสดง / โมดูล [สิ่งนี้มืดมนขึ้นอยู่กับภาษา]
  • ระดับวิธีการ / ฟังก์ชั่น

และอื่น ๆ ถึงโทเค็นของแต่ละบุคคล

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


2

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

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

ตัวอย่างหนึ่งของวิธีการนี้อยู่ใน Python builtin tupleและfrozensetชนิดข้อมูล นี่คือการใช้งานทั่วไปของ frozenet:

small_digits = frozenset([0, 1, 2, 3, 4])
big_digits = frozenset([5, 6, 7, 8, 9])
all_digits = small_digits.union(big_digits)

print("small:", small_digits)
print("big:", big_digits)
print("all:", all_digits)

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

เล็ก: frozenset ({0, 1, 2, 3, 4})

ใหญ่: frozenset ({5, 6, 7, 8, 9})

ทั้งหมด: frozenset ({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})

อีกตัวอย่างที่กว้างขวางของโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปแบบที่คล้ายกันคือห้องสมุดImmutable.jsของ Facebook ในทั้งสองกรณีคุณเริ่มต้นด้วย Building Block เหล่านี้และสามารถสร้างออบเจ็กต์โดเมนระดับสูงกว่าซึ่งเป็นไปตามหลักการเดียวกันได้มาซึ่งวิธีการ OOP ที่ใช้งานได้ซึ่งช่วยให้คุณสามารถห่อหุ้มข้อมูลและเหตุผลเกี่ยวกับมันได้ง่ายขึ้น และความไม่สามารถเปลี่ยนแปลงได้ยังช่วยให้คุณได้รับประโยชน์จากความสามารถในการแชร์ออบเจ็กต์ระหว่างเธรดโดยไม่ต้องกังวลเกี่ยวกับการล็อค


1

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

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

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

หากเราใช้ตัวอย่างสัญญาณไฟจราจรจากนั้นความพยายามที่ไร้เดียงสาในทันทีต้องการให้ความรู้เกี่ยวกับสัญญาณไฟจราจรดังกล่าวเกี่ยวกับโลกทั้งโลกที่ล้อมรอบมัน ดังนั้นถ้าฉันเข้าใจอย่างถูกต้องคุณสรุปว่าที่อยู่ห่าง ๆ และ decouple สนับสนุนแนวคิดทั่วไปของพอร์ต I / O ที่เผยแพร่ข้อความและคำขอเพิ่มเติมไม่ใช่ข้อมูลผ่านไปป์ไลน์ ในขณะที่ลืมกันและกัน

ท่อปม

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

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

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

มันทำงานอย่างไร

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

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

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

กรณีใช้งานจริง

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

สัญญาณและช่อง

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


0

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

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


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