ฟังก์ชั่นการเขียนโปรแกรมเปรียบเทียบกับ OOP กับคลาส


32

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

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

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

สำหรับการอ้างอิงประสบการณ์หลักของฉันกับ OOP คือ Python, PHP และ C # บางอย่าง ภาษาที่ฉันกำลังดูที่มีคุณสมบัติการใช้งานคือ Scala และ Haskell แม้ว่าฉันจะเอนไปทางสกาล่า

ตัวอย่างพื้นฐาน (Python):

Animal(object):
    def say(self, what):
        print(what)

Dog(Animal):
    def say(self, what):
        super().say('dog barks: {0}'.format(what))

Cat(Animal):
    def say(self, what):
        super().say('cat meows: {0}'.format(what))

dog = Dog()
cat = Cat()
dog.say('ruff')
cat.say('purr')

Scala ได้รับการออกแบบเป็น OOP + FP ดังนั้นคุณไม่จำเป็นต้องเลือก
Karthik T

1
ใช่ฉันรู้ แต่ฉันก็อยากรู้ด้วยเหตุผลทางปัญญาเช่นกัน ฉันไม่พบสิ่งใดเทียบเท่าวัตถุในภาษาที่ใช้งานได้สำหรับ scala ฉันยังต้องการทราบว่าเมื่อใด / ที่ไหน / อย่างไรที่ฉันควรใช้การทำงานมากกว่า oop แต่ IMHO เป็นคำถามอื่น
skift

2
"โดยเฉพาะอย่างยิ่งที่เน้นมากเกินไป IMO เป็นแนวคิดที่เราไม่รักษาไว้": นี่คือความเชื่อที่ผิด ไม่เป็นความจริงที่ FP จะไม่ใช้ state แต่ FP จะจัดการกับรัฐในลักษณะที่แตกต่างกัน (เช่น monads ใน Haskell หรือประเภทเฉพาะใน Clean)
Giorgio


3
อาจเป็นไปได้ซ้ำกับFunction Programming vs. OOP
Caleb

คำตอบ:


21

สิ่งที่คุณถามจริงๆเกี่ยวกับที่นี่คือวิธีทำPolymorphismในภาษาที่ใช้งานได้เช่นวิธีสร้างฟังก์ชันที่ทำงานแตกต่างกันไปตามอาร์กิวเมนต์ของพวกเขา

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

ภาษาที่ใช้งานโดยทั่วไปมีตัวเลือกต่าง ๆ สำหรับการบรรลุความหลากหลาย:

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

ตัวอย่างเช่นต่อไปนี้เป็นการนำ Clojure ของปัญหาของคุณโดยใช้วิธีการหลายวิธี:

;; define a multimethod, that dispatched on the ":type" keyword
(defmulti say :type)  

;; define specific methods for each possible value of :type. You can add more later
(defmethod say :cat [animal what] (println (str "Car purrs: " what)))
(defmethod say :dog [animal what] (println (str "Dog barks: " what)))
(defmethod say :default [animal what] (println (str "Unknown noise: " what)))

(say {:type :dog} "ruff")
=> Dog barks: ruff

(say {:type :ape} "ook")
=> Unknown noise: ook

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


ไม่ชัดเจน 100% แต่เพียงพอที่จะดูว่าคุณกำลังจะไปไหน ฉันเห็นว่านี่เป็นรหัส 'สัตว์' ในไฟล์ที่กำหนด ส่วนในการแตกแขนง / เงื่อนไขก็ดีเช่นกัน ฉันไม่ได้พิจารณาว่าเป็นทางเลือกหาก / อื่น ๆ
skift

11

นี่ไม่ใช่คำตอบโดยตรงและไม่จำเป็นต้องแม่นยำ 100% เพราะฉันไม่ใช่ผู้เชี่ยวชาญด้านภาษา แต่ในกรณีใดฉันจะแบ่งปันประสบการณ์ของคุณ ...

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

สำหรับฉัน "การพัฒนา" มาเมื่อฉันตัดสินใจที่จะเรียนรู้หลาม ฉันดาวน์โหลดไพ ธ อนจากนั้นไปที่หน้าแรกของโครงการออยเลอร์และเพิ่งเริ่มทำปัญหาอย่างใดอย่างหนึ่งหลังจากนั้นอีกหนึ่ง Python ไม่จำเป็นต้องเป็นภาษา FP และแน่นอนว่าคุณสามารถสร้างคลาสได้ แต่เมื่อเทียบกับ C ++ / Java / C # มันมีโครงสร้าง FP มากขึ้นดังนั้นเมื่อฉันเริ่มเล่นกับมันฉันตัดสินใจอย่างมีสติที่จะไม่ กำหนดชั้นเรียนเว้นแต่ฉันจะต้อง

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

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

กุญแจสำคัญของ FP คือคุณไม่มีผลข้างเคียง ตราบใดที่คุณปฏิบัติต่อแอปพลิเคชันเป็นการแปลงข้อมูลอย่างง่ายด้วยชุดอินพุตและชุดผลลัพธ์ที่กำหนดคุณสามารถเขียนรหัส FP ที่จะทำให้สิ่งที่คุณต้องการบรรลุผลสำเร็จ เห็นได้ชัดว่าไม่ใช่ทุกแอปพลิเคชันจะพอดีกับรานี้ แต่เมื่อคุณเริ่มทำมันคุณจะประหลาดใจว่ามีแอพพลิเคชั่นกี่ตัวที่เหมาะสม และนี่คือที่ฉันคิดว่า Python, F # หรือ Scala เปล่งประกายเพราะพวกมันให้โครงสร้าง FP แก่คุณ แต่เมื่อคุณจำเป็นต้องจำสถานะของคุณและ "แนะนำผลข้างเคียง" คุณสามารถถอยกลับไปใช้เทคนิค OOP ที่แท้จริง

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

สัปดาห์ที่แล้วฉันเริ่มเขียนโค้ดใน Java และตั้งแต่นั้นมาเกือบทุกวันฉันได้รับการเตือนว่าเมื่ออยู่ใน OOP ฉันต้องใช้อินเทอร์เฟซโดยประกาศคลาสด้วยวิธีที่แทนที่ฟังก์ชั่นในบางกรณีฉันสามารถใช้ Python ได้ การแสดงออกแลมบ์ดาอย่างง่ายเช่นรหัส 20-30 บรรทัดที่ฉันเขียนเพื่อสแกนไดเรกทอรีจะเป็น 1-2 บรรทัดใน Python และไม่มีคลาส

FP ตัวเองเป็นภาษาระดับที่สูงขึ้น ใน Python (ขออภัยประสบการณ์ FP ของฉันเท่านั้น) ฉันสามารถรวบรวม list comprehension ไว้ใน list comprehension ด้วย lambdas และสิ่งของอื่น ๆ ที่ถูกส่งเข้ามาและสิ่งทั้งหมดจะเป็นเพียง 3-4 บรรทัดของโค้ด ใน C ++ ฉันสามารถทำสิ่งเดียวกันได้อย่างสมบูรณ์ แต่เนื่องจาก C ++ อยู่ในระดับต่ำกว่าฉันจะต้องเขียนโค้ดมากกว่า 3-4 บรรทัดและเมื่อจำนวนของบรรทัดเพิ่มขึ้นการฝึกอบรม SRP ของฉันจะเริ่มขึ้นและฉันจะเริ่มต้น คิดเกี่ยวกับวิธีการแบ่งรหัสเป็นชิ้นเล็ก ๆ (เช่นฟังก์ชั่นอื่น ๆ อีกมากมาย) แต่เพื่อผลประโยชน์ของการบำรุงรักษาและการซ่อนรายละเอียดการใช้งานฉันจะใส่ฟังก์ชั่นเหล่านั้นทั้งหมดในชั้นเรียนเดียวกันและทำให้พวกเขาเป็นส่วนตัว และที่นั่นคุณมี ... ฉันเพิ่งสร้างชั้นเรียนในขณะที่ในหลามฉันจะได้เขียน "return (.... lambda x: .. .... )"


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

คำตอบที่ดี พูดกับทุกคนที่ยืนอยู่ข้างสระว่ายน้ำที่ทำงาน แต่ไม่แน่ใจว่าจะแช่เท้าในน้ำหรือไม่
Robben_Ford_Fan_boy

และทับทิม ... หนึ่งในปรัชญาการออกแบบของมันมุ่งเน้นไปที่วิธีการบล็อกรหัสเป็นอาร์กิวเมนต์โดยทั่วไปเป็นตัวเลือก และเมื่อได้รับความคิดทางไวยากรณ์ที่สะอาดและการเขียนโค้ดด้วยวิธีนี้เป็นเรื่องง่าย มันยากที่จะคิดและแต่งทำนองนี้ใน C # ฟังก์ชั่น C # writ เป็น verbose และสับสนมันรู้สึก shoehorned เป็นภาษา ฉันชอบที่ Ruby ช่วยให้การคิดง่ายขึ้นเพื่อให้เห็นศักยภาพในกล่องคิด C # ของฉัน ในการวิเคราะห์ขั้นสุดท้ายฉันเห็นหน้าที่และ OO เป็นส่วนเสริม ฉันว่าทับทิมคิดอย่างนั้น
Radarbob

8

ใน Haskell สิ่งที่ใกล้เคียงที่สุดคือ "คลาส" คลาสนี้แม้ว่าจะไม่เหมือนกับคลาสใน Java และ C ++จะทำงานกับสิ่งที่คุณต้องการในกรณีนี้

ในกรณีของคุณนี่คือลักษณะรหัสของคุณ

ชั้นเรียนสัตว์ 
พูด :: สตริง -> เสียง 

จากนั้นคุณสามารถมีประเภทข้อมูลส่วนบุคคลที่ปรับวิธีการเหล่านี้

เช่นสัตว์เลี้ยงสุนัขที่ไหน
say s = "bark" ++ s 

แก้ไข: - ก่อนที่คุณจะสามารถพูดได้สำหรับสุนัขคุณต้องบอกระบบว่า Dog เป็นสัตว์

ข้อมูล Dog = \ - บางอย่างที่นี่ - \ (ได้รับสัตว์)

แก้ไข: - สำหรับ Wilq
ตอนนี้ถ้าคุณต้องการที่จะใช้พูดในฟังก์ชั่นบอกว่า foo คุณจะต้องบอกฮาเซลว่า foo สามารถทำงานกับสัตว์ได้

foo :: (Animal a) => a -> String -> String
foo a str = พูดว่า str 

ตอนนี้ถ้าคุณเรียกฟูกับหมามันจะเห่าถ้าคุณโทรกับแมวมันจะเหมียว

main = do 
ให้ d = dog (\ - พารามิเตอร์ cstr - \)
    c = cat  
ในการแสดง $ foo d "Hello World"

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


ป่วยต้องจริงเพิ่มเติมเล็กน้อยบน haskell เข้าใจ thi อย่างเต็มที่ แต่ฉันคิดว่าฉันได้รับเรื่องตลกของมัน ฉันยังสงสัยอยู่ว่ามันจะสอดคล้องกับฐานรหัสที่ซับซ้อนได้อย่างไร
skift

nitpickสัตว์ควรเป็นตัวพิมพ์ใหญ่
Daniel Gratzer

1
ฟังก์ชั่น say รู้ได้อย่างไรว่าคุณเรียกมันว่า Dog ถ้าใช้ String เท่านั้น? และไม่ "ได้รับ" สำหรับบางคลาสที่มีอยู่แล้วเท่านั้นหรือ
WilQu

6

ฟังก์ชั่นภาษาใช้ 2 โครงสร้างเพื่อให้เกิดความหลากหลาย:

  • ฟังก์ชั่นการสั่งซื้อครั้งแรก
  • generics

การสร้างรหัส polymorphic นั้นแตกต่างจากวิธีการใช้ OOP โดยสิ้นเชิงและวิธีการเสมือน ในขณะที่ทั้งสองอย่างนั้นอาจมีให้บริการในภาษา OOP ที่คุณชื่นชอบ (เช่น C #) ภาษาที่ใช้งานได้ส่วนใหญ่ (เช่น Haskell) จะเพิ่มเป็นสิบเอ็ด เป็นการยากที่ฟังก์ชันจะไม่ใช่แบบทั่วไปและฟังก์ชันส่วนใหญ่มีหน้าที่เป็นพารามิเตอร์

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


2
OOP เป็นเรื่องของ polymorphism ถ้าคุณคิดว่า OOP เกี่ยวกับการมีฟังก์ชั่นเชื่อมโยงกับข้อมูลของคุณคุณก็ไม่รู้อะไรเกี่ยวกับ OOP
ร่าเริง

4
ความแตกต่างเป็นเพียงแง่มุมหนึ่งของ OOP และฉันคิดว่าไม่ใช่ OP ที่ถามจริงๆ
Doc Brown

2
ความแตกต่างเป็นลักษณะสำคัญของ OOP ทุกอย่างอยู่ที่นั่นเพื่อสนับสนุน OOP ที่ไม่มีกรรมวิธี / วิธีเสมือนเกือบจะเหมือนกับการเขียนโปรแกรมแบบขั้นตอนทั้งหมด
ร่าเริง

1
@ErikReppen หากไม่จำเป็นต้องใช้ "ใช้งานอินเทอร์เฟซ" แสดงว่าคุณไม่ได้ทำ OOP นอกจากนี้ Haskell ยังมีโมดูลด้วย
ร่าเริง

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

0

มันขึ้นอยู่กับสิ่งที่คุณต้องการบรรลุ

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

def bark(what):
    print "barks: {0}".format(what) 

def meow(what):
    print "meows: {0}".format(what)

def climb(how):
    print "climbs: {0}".format(how)

if __name__ == "__main__":
    animals = {'dog': {'say': bark},
               'cat': {'say': meow,
                       'climb': climb}}
    animals['dog']['say']("ruff")
    animals['cat']['say']("purr")
    animals['cat']['climb']("well")

อย่างไรก็ตามโปรดทราบว่า (a) ไม่มี 'อินสแตนซ์' ของสุนัขหรือแมวและ (b) คุณจะต้องติดตาม 'ประเภท' ของวัตถุของคุณเอง

เช่นตัวอย่าง: pets = [['martin','dog','grrrh'], ['martha', 'cat', 'zzzz']]. จากนั้นคุณสามารถทำรายการความเข้าใจเช่น[animals[pet[1]]['say'](pet[2]) for pet in pets]


0

ภาษา OO สามารถใช้แทนภาษาระดับต่ำในบางครั้งเพื่อเชื่อมต่อโดยตรงกับเครื่อง C ++ แน่นอน แต่สำหรับ C # ก็มีอะแดปเตอร์และเช่นนั้น แม้ว่าการเขียนรหัสเพื่อควบคุมชิ้นส่วนเครื่องจักรกลและมีการควบคุมหน่วยความจำนาทีจะดีที่สุดใกล้เคียงกับระดับต่ำที่สุด แต่ถ้าคำถามนี้เกี่ยวข้องกับซอฟต์แวร์เชิงวัตถุในปัจจุบันเช่น Line Of Business, เว็บแอพพลิเคชั่น, IOT, Web Services และแอปพลิเคชั่นที่ใช้งานมากที่สุด ...

ตอบถ้ามี

ผู้อ่านอาจลองทำงานกับ Service-Oriented Architecture (SOA) นั่นคือ DDD, N-Layered, N-Tiered, Hexagonal อะไรก็ตาม ฉันไม่ได้เห็นแอปพลิเคชันธุรกิจขนาดใหญ่ใช้ "OO" แบบดั้งเดิม "อย่างมีประสิทธิภาพ (Active-Record หรือ Rich-Models) อย่างที่อธิบายไว้ในยุค 70 และ 80 ในทศวรรษที่ผ่านมา + (ดูหมายเหตุ 1)

ความผิดไม่ใช่ของ OP แต่มีปัญหาสองสามข้อกับคำถาม

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

  2. ใน FP และ SOA ข้อมูลจะถูกแยกออกจาก Business Logic นั่นคือข้อมูลและลอจิกไม่ทำงานร่วมกัน ลอจิกเข้าสู่บริการและ Data (Domain Models) ไม่มีพฤติกรรม Polymorphic (ดูหมายเหตุ 2)

  3. บริการและฟังก์ชั่นสามารถ Polymorphic ใน FP คุณมักส่งผ่านฟังก์ชันเป็นพารามิเตอร์ไปยังฟังก์ชันอื่นแทนค่า คุณสามารถทำสิ่งเดียวกันใน OO ภาษาที่มีประเภทเช่น Callable หรือ Func แต่มันไม่ทำงานอาละวาด (ดูหมายเหตุ 3) ใน FP และ SOA โมเดลของคุณไม่ใช่ Polymorphic เฉพาะบริการ / ฟังก์ชันของคุณ (ดูหมายเหตุ 4)

  4. มีกรณีที่ไม่ดีของการเข้ารหัสในตัวอย่างนั้น ฉันไม่เพียง แต่พูดถึงสตริงสีแดง "หมาเห่า" ฉันยังพูดถึง CatModel และ DogModel ด้วย จะเกิดอะไรขึ้นเมื่อคุณต้องการเพิ่มแกะ คุณต้องเข้าไปในรหัสของคุณและสร้างรหัสใหม่? ทำไม? ในรหัสการผลิตฉันอยากเห็นเพียง AnimalModel ที่มีคุณสมบัติของมัน ที่เลวร้ายที่สุด AmphibianModel และ FowlModel หากคุณสมบัติและการจัดการแตกต่างกันมาก

นี่คือสิ่งที่ฉันคาดว่าจะเห็นในภาษา "OO" ปัจจุบัน:

public class Animal
{
    public int AnimalID { get; set; }
    public int LegCount { get; set; }
    public string Name { get; set; }
    public string WhatISay { get; set; }
}

public class AnimalService : IManageAnimals
{
    private IPersistAnimals _animalRepo;
    public AnimalService(IPersistAnimals animalRepo) { _animalRepo = animalRepo; }

    public List<Animal> GetAnimals() => _animalRepo.GetAnimals();

    public string WhatDoISay(Animal animal)
    {
        if (!string.IsNullOrWhiteSpace(animal.WhatISay))
            return animal.WhatISay;

        return _animalRepo.GetAnimalNoise(animal.AnimalID);
    }
}

การไหลพื้นฐาน

คุณย้ายจาก Classes ใน OO ไปยัง Function Programming ได้อย่างไร อย่างที่คนอื่นพูด คุณทำได้ แต่คุณทำไม่ได้จริงๆ ประเด็นข้างต้นคือการแสดงให้เห็นว่าคุณไม่ควรใช้คลาส (ในความหมายดั้งเดิมของโลก) เมื่อใช้งาน Java และ C # เมื่อคุณเขียนโค้ดในสถาปัตยกรรมที่มุ่งเน้นการบริการ (DDD, Layered, Tiered, Hexagonal, อะไรก็ตาม) คุณจะเข้าใกล้ฟังก์ชั่นการทำงานเพียงขั้นตอนเดียวเนื่องจากคุณแยกข้อมูล (โมเดลโดเมน) ออกจากฟังก์ชันตรรกะ (Services)

ภาษา OO ใกล้ FP

คุณอาจต้องใช้เวลาเพิ่มอีกนิดและแยก SOA Services ออกเป็นสองประเภท

Class Class แบบเลือกได้ 1 : บริการปรับใช้อินเตอร์เฟสทั่วไปสำหรับจุดเข้าใช้งาน สิ่งเหล่านี้จะเป็นจุดเข้าใช้งาน "ไม่บริสุทธิ์" ซึ่งสามารถเรียกใช้ฟังก์ชันอื่น ๆ ของ "บริสุทธิ์" หรือ "ไม่บริสุทธิ์" นี่อาจเป็นจุดเข้าใช้งานของคุณจาก RESTful API

ตัวเลือก Class 2 : บริการทางธุรกิจบริสุทธิ์ เหล่านี้เป็นคลาสที่คงที่ซึ่งมีฟังก์ชั่น "เพียว" ใน FP "บริสุทธิ์" หมายถึงไม่มีผลข้างเคียง มันไม่ได้ตั้งค่ารัฐหรือการมีอยู่อย่างชัดเจนทุกที่ (ดูหมายเหตุ 5)

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

หมายเหตุ

หมายเหตุ 1 : การออกแบบเชิงวัตถุ "Rich" หรือ "Active-Record" ดั้งเดิมยังคงอยู่ มีรหัสดั้งเดิมมากมายเช่นนั้นเมื่อผู้คน "ทำถูกต้อง" เมื่อสิบปีที่แล้ว ครั้งสุดท้ายที่ฉันเห็นโค้ดประเภทนั้น (ทำอย่างถูกต้อง) มาจากวิดีโอเกม Codebase ใน C ++ ที่ซึ่งพวกเขาควบคุมหน่วยความจำอย่างแม่นยำและมีพื้นที่ จำกัด มาก อย่าบอกว่า FP และ Service-Oriented Architecture เป็นสัตว์ร้ายและไม่ควรพิจารณาถึงฮาร์ดแวร์ แต่พวกเขามีความสามารถในการเปลี่ยนแปลงอย่างต่อเนื่องได้รับการดูแลรักษามีขนาดข้อมูลที่เปลี่ยนแปลงและด้านอื่น ๆ เป็นลำดับความสำคัญ ในวิดีโอเกมและเครื่อง AI คุณสามารถควบคุมสัญญาณและข้อมูลได้อย่างแม่นยำมาก

หมายเหตุ 2 : รูปแบบโดเมนไม่มีพฤติกรรมแบบ Polymorphic และไม่มีการพึ่งพาภายนอก พวกเขาคือ "โดดเดี่ยว" ไม่ได้หมายความว่าพวกเขาจะต้องเป็นโรคโลหิตจาง 100% พวกเขาสามารถมีเหตุผลมากมายที่เกี่ยวข้องกับการก่อสร้างและการเปลี่ยนแปลงคุณสมบัติที่ไม่แน่นอนถ้าใช้เช่นนั้น ดู DDD "วัตถุค่า" และหน่วยงานโดย Eric Evans และ Mark Seemann

หมายเหตุ 3 : Linq และ Lambda นั้นเป็นเรื่องธรรมดามาก แต่เมื่อผู้ใช้สร้างฟังก์ชั่นใหม่พวกเขาไม่ค่อยใช้ Func หรือ Callable เป็นพารามิเตอร์ในขณะที่ใน FP มันจะแปลกที่จะเห็นแอพที่ไม่มีฟังก์ชั่นตามรูปแบบนั้น

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

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


-2

คุณสามารถทำอะไรเช่นนี้ .. php

    function say($whostosay)
    {
        if($whostosay == 'cat')
        {
             return 'purr';
        }elseif($whostosay == 'dog'){
             return 'bark';
        }else{
             //do something with errors....
        }
     }

     function speak($whostosay)
     {
          return $whostosay .'\'s '.say($whostosay);
     }
     echo speak('cat');
     >>>cat's purr
     echo speak('dog');
     >>>dogs's bark

1
ฉันไม่ได้รับคะแนนลบ แต่ฉันเดาว่ามันเป็นเพราะวิธีนี้ไม่ได้ใช้งานได้ดี
Manoj R

1
แต่แนวคิดที่สื่อความหมายนั้นใกล้เคียงกับการจับคู่รูปแบบที่ใช้ในการเขียนโปรแกรมการทำงานคือ$whostosayกลายเป็นประเภทวัตถุที่กำหนดสิ่งที่จะถูกดำเนินการ ด้านบนสามารถแก้ไขได้เพื่อยอมรับพารามิเตอร์อื่นเพิ่มเติม$whattosayเพื่อให้ประเภทที่รองรับ (เช่น'human') สามารถใช้ประโยชน์ได้
syockit
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.