portaldacalheta.pt
  • หลัก
  • การจัดการวิศวกรรม
  • Kpi และ Analytics
  • เทคโนโลยี
  • ว่องไว
ส่วนหลัง

4 ไปวิจารณ์ภาษา



Go (a.k.a. Golang) เป็นหนึ่งในภาษาที่ผู้คนให้ความสนใจมากที่สุด ณ เดือนเมษายน 2018 อยู่ในอันดับที่ 19 ในดัชนี TIOBE . ผู้คนจำนวนมากขึ้นเรื่อย ๆ เปลี่ยนจาก PHP, Node.js และภาษาอื่น ๆ มาใช้ Go และใช้มันในการผลิต ซอฟต์แวร์เจ๋ง ๆ มากมาย (เช่น Kubernetes, Docker และ Heroku CLI) เขียนโดยใช้ Go

แล้วอะไรคือกุญแจสู่ความสำเร็จของ Go มีหลายสิ่งหลายอย่างในภาษาที่ทำให้มันเจ๋งมาก แต่สิ่งสำคัญอย่างหนึ่งที่ทำให้ Go เป็นที่นิยมคือความเรียบง่าย ตามที่ Rob Pike ผู้สร้างคนหนึ่งชี้ให้เห็น .



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



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



คำวิจารณ์ภาษา Go 4

1. ขาดการทำงานเกินพิกัดและค่าดีฟอลต์สำหรับอาร์กิวเมนต์

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

func (wd *remoteWD) WaitWithTimeoutAndInterval(condition Condition, timeout, interval time.Duration) error { // the actual implementation was here } func (wd *remoteWD) WaitWithTimeout(condition Condition, timeout time.Duration) error { return wd.WaitWithTimeoutAndInterval(condition, timeout, DefaultWaitInterval) } func (wd *remoteWD) Wait(condition Condition) error { return wd.WaitWithTimeoutAndInterval(condition, DefaultWaitTimeout, DefaultWaitInterval) }

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



ผม

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



จะปรับปรุงได้อย่างไร?

นี่คือตัวอย่างเดียวกัน (เช่นเดียวกัน) ใน JavaScript:

ตัวอย่างการจัดทำงบประมาณทุนพร้อมแนวทางแก้ไข
function Wait (condition, timeout = DefaultWaitTimeout, interval = DefaultWaitInterval) { // actual implementation here }

อย่างที่คุณเห็นมันดูชัดเจนขึ้นมาก



ฉันชอบวิธี Elixir ด้วยเช่นกัน นี่คือลักษณะที่ปรากฏใน Elixir (ฉันรู้ว่าฉันสามารถใช้ค่าเริ่มต้นได้ดังตัวอย่างด้านบน - ฉันแค่แสดงให้เห็นว่ามันเป็นวิธีที่สามารถทำได้):

defmodule Waiter do @default_interval 1 @default_timeout 10 def wait(condition, timeout, interval) do // implementation here end def wait(condition, timeout), do: wait(condition, timeout, @default_interval) def wait(condition), do: wait(condition, @default_timeout, @default_interval) end Waiter.wait('condition', 2, 20) Waiter.wait('condition', 2) Waiter.wait('condition')

2. ขาด Generics

นี่เป็นเนื้อหาที่ผู้ใช้ Go กำลังถามหามากที่สุด



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

มาทำแทนจำนวนเต็ม:



package main import 'fmt' func mapArray(arr []int, callback func (int) (int)) []int { newArray := make([]int, len(arr)) for index, value := range arr { newArray[index] = callback(value) } return newArray; } func main() { square := func(x int) int { return x * x } fmt.Println(mapArray([]int{1,2,3,4,5}, square)) // prints [1 4 9 16 25] }

ดูดีใช่มั้ย?

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

func mapArrayOfInts(arr []int, callback func (int) (int)) []int { // implementation } func mapArrayOfFloats(arr []float64, callback func (float64) (float64)) []float64 { // implementation } func mapArrayOfStrings(arr []string, callback func (string) (string)) []string { // implementation }

นั่นขัดกับหลักการ DRY (Don’t Repeat Yourself) อย่างแน่นอนซึ่งระบุว่าคุณต้องเขียนโค้ดคัดลอก / วางให้น้อยที่สุดเท่าที่จะเป็นไปได้และย้ายไปที่ฟังก์ชันแล้วนำมาใช้ใหม่แทน

การขาดข้อมูลทั่วไปหมายถึงฟังก์ชันที่แตกต่างกันหลายร้อยฟังก์ชัน

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

วิธีการออกแบบต้นแบบ

จะปรับปรุงได้อย่างไร?

มีภาษาดีๆมากมายที่รองรับภาษาทั่วไป ตัวอย่างเช่นนี่คือรหัสเดียวกันใน Rust (ฉันเคยใช้ vec แทน array เพื่อให้ง่ายขึ้น):

fn map(vec:Vec, callback:fn(T) -> T) -> Vec { let mut new_vec = vec![]; for value in vec { new_vec.push(callback(value)); } return new_vec; } fn square (val:i32) -> i32 { return val * val; } fn underscorify(val:String) -> String { return format!('_{}_', val); } fn main() { let int_vec = vec![1, 2, 3, 4, 5]; println!('{:?}', map::(int_vec, square)); // prints [1, 4, 9, 16, 25] let string_vec = vec![ 'hello'.to_string(), 'this'.to_string(), 'is'.to_string(), 'a'.to_string(), 'vec'.to_string() ]; println!('{:?}', map::(string_vec, underscorify)); // prints ['_hello_', '_this_', '_is_', '_a_', '_vec_'] }

โปรดทราบว่ามีการใช้งาน map เพียงครั้งเดียว ฟังก์ชั่นและสามารถใช้ได้กับทุกประเภทที่คุณต้องการแม้แต่แบบกำหนดเอง

3. การจัดการการพึ่งพา

ใครก็ตามที่มีประสบการณ์ใน Go บอกได้เลยว่าการจัดการการพึ่งพานั้นยากจริงๆ Go tools อนุญาตให้ผู้ใช้ติดตั้งไลบรารีต่างๆโดยเรียกใช้ go get ปัญหาคือการจัดการเวอร์ชัน หากผู้ดูแลไลบรารีทำการเปลี่ยนแปลงที่เข้ากันไม่ได้ย้อนหลังและอัปโหลดไปยัง GitHub ใครก็ตามที่พยายามใช้โปรแกรมของคุณหลังจากนั้นจะได้รับข้อผิดพลาดเนื่องจาก go get ไม่ทำอะไรเลยนอกจาก git clone ที่เก็บของคุณลงในโฟลเดอร์ไลบรารี นอกจากนี้หากไม่ได้ติดตั้งไลบรารีโปรแกรมจะไม่คอมไพล์ด้วยเหตุนี้

คุณสามารถทำได้ดีขึ้นเล็กน้อยโดยใช้ Dep เพื่อจัดการการอ้างอิง ( https://github.com/golang/dep ) แต่ปัญหาที่นี่คือคุณกำลังจัดเก็บการอ้างอิงทั้งหมดของคุณบนที่เก็บของคุณ (ซึ่งไม่ดีเพราะที่เก็บของคุณไม่เพียง แต่มีโค้ดของคุณเท่านั้น แต่ยังมีโค้ดอ้างอิงหลายพันบรรทัด) หรือเพียงแค่เก็บรายการแพ็คเกจ ( แต่อีกครั้งหากผู้ดูแลการอ้างอิงทำการเปลี่ยนแปลงที่เข้ากันไม่ได้ย้อนหลังการเปลี่ยนแปลงทั้งหมดจะผิดพลาด)

จะปรับปรุงได้อย่างไร?

ฉันคิดว่าตัวอย่างที่สมบูรณ์แบบในที่นี้คือ Node.js (และ JavaScript โดยทั่วไปฉันคิดว่า) และ NPM NPM เป็นที่เก็บแพ็กเกจ โดยจะจัดเก็บแพ็กเกจเวอร์ชันต่างๆดังนั้นหากคุณต้องการแพ็กเกจเวอร์ชันเฉพาะก็ไม่มีปัญหาคุณสามารถดาวน์โหลดได้จากที่นั่น นอกจากนี้สิ่งหนึ่งในแอปพลิเคชัน Node.js / JavaScript คือ package.json ไฟล์. ที่นี่การอ้างอิงทั้งหมดและเวอร์ชันของพวกเขาจะแสดงรายการดังนั้นคุณสามารถติดตั้งได้ทั้งหมด (และรับเวอร์ชันที่ใช้งานได้กับโค้ดของคุณอย่างแน่นอน) ด้วย npm install

นอกจากนี้ตัวอย่างที่ดีของการจัดการแพ็คเกจ ได้แก่ RubyGems / Bundler (สำหรับแพ็คเกจ Ruby) และ Crates.io/Cargo (สำหรับไลบรารี Rust)

4. การจัดการข้อผิดพลาด

การจัดการข้อผิดพลาดใน Go นั้นง่ายมาก ใน Go โดยทั่วไปคุณสามารถคืนค่าหลายค่าจากฟังก์ชันและฟังก์ชันสามารถส่งคืนข้อผิดพลาดได้ สิ่งนี้:

err, value := someFunction(); if err != nil { // handle it somehow }

ตอนนี้ลองนึกดูว่าคุณต้องเขียนฟังก์ชันที่ทำสามการกระทำที่ส่งคืนข้อผิดพลาด จะมีลักษณะดังนี้:

func doSomething() (err, int) { err, value1 := someFunction(); if err != nil { return err, nil } err, value2 := someFunction2(value1); if err != nil { return err, nil } err, value3 := someFunction3(value2); if err != nil { return err, nil } return value3; }

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

ภาพตลกของรหัสการจัดการข้อผิดพลาดบนแป้นพิมพ์

จะปรับปรุงได้อย่างไร?

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

การขุดข้อมูล twitter ด้วย python
function doStuff() { const value1 = someFunction(); const value2 = someFunction2(value1); const value3 = someFunction3(value2); return value3; } try { const value = doStuff(); // do something with it } catch (err) { // handle the error }

เป็นวิธีที่ชัดเจนมากขึ้นและไม่มีรหัสที่ทำซ้ำได้สำหรับการจัดการข้อผิดพลาด

การเพิ่มประสิทธิภาพการค้นหาใน sql server 2012

สิ่งที่ดีในการไป

แม้ว่า Go จะมีข้อบกพร่องมากมายจากการออกแบบ แต่ก็มีคุณสมบัติที่ยอดเยี่ยมเช่นกัน

1. โกรูทีน

การเขียนโปรแกรม Async ทำได้ง่ายมากใน Go ในขณะที่การเขียนโปรแกรมแบบมัลติเธรดมักทำได้ยากในภาษาอื่น ๆ แต่การสร้างเธรดใหม่และเรียกใช้ฟังก์ชันในนั้นจึงไม่บล็อกเธรดปัจจุบันนั้นง่ายมาก:

func doSomeCalculations() { // do some CPU intensive/long running tasks } func main() { go doSomeCalculations(); // This will run in another thread; }

2. เครื่องมือที่มาพร้อมกับ Go

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

  • gofmt - เครื่องมือสำหรับการวิเคราะห์รหัสคงที่ เมื่อเปรียบเทียบกับ JavaScript ที่คุณต้องติดตั้งการอ้างอิงเพิ่มเติมเช่น eslint หรือ jshint โดยค่าเริ่มต้นจะรวมไว้ที่นี่ และโปรแกรมจะไม่คอมไพล์ด้วยซ้ำหากคุณไม่ได้เขียนโค้ด Go-style (ไม่ใช้ตัวแปรที่ประกาศการนำเข้าแพ็กเกจที่ไม่ได้ใช้งาน ฯลฯ )
  • go test - กรอบการทดสอบ อีกครั้งเมื่อเปรียบเทียบกับ JavaScript คุณต้องติดตั้งการอ้างอิงเพิ่มเติมสำหรับการทดสอบ (Jest, Mocha, AVA ฯลฯ ) ที่นี่จะรวมไว้โดยค่าเริ่มต้น และช่วยให้คุณสามารถทำสิ่งดีๆมากมายตามค่าเริ่มต้นเช่นการเปรียบเทียบการแปลงรหัสในเอกสารเป็นแบบทดสอบเป็นต้น
  • godoc - เครื่องมือจัดทำเอกสาร เป็นเรื่องดีที่จะรวมไว้ในเครื่องมือเริ่มต้น
  • คอมไพเลอร์นั่นเอง มันเร็วมากอย่างไม่น่าเชื่อเมื่อเทียบกับภาษาคอมไพล์อื่น ๆ !

3. เลื่อน

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

function openManyFiles() { let file1, file2, file3; try { file1 = open(‘path-to-file1’); } catch (err) { return; } try { file2 = open(‘path-to-file2’); } catch (err) { // we need to close first file, remember? close(file1); return; } try { file3 = open(‘path-to-file3’); } catch (err) { // and now we need to close both first and second file close(file1); close(file2); return; } // do some stuff with files // closing files after successfully processing them close(file1); close(file2); close(file3); return; }

ดูซับซ้อน. นั่นคือที่มาของ Go defer เข้ามาแทนที่:

package main import ( 'fmt' ) func openFiles() { // Pretending we’re opening files fmt.Printf('Opening file 1 '); defer fmt.Printf('Closing file 1 '); fmt.Printf('Opening file 2 '); defer fmt.Printf('Closing file 2 '); fmt.Printf('Opening file 3 '); // Pretend we've got an error on file opening // In real products, an error will be returned here. return; } func main() { openFiles() /* Prints: Opening file 1 Opening file 2 Opening file 3 Closing file 2 Closing file 1 */ }

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

สรุป

ฉันไม่ได้พูดถึงสิ่งที่ดีและไม่ดีทั้งหมดใน Go เพียงแค่สิ่งที่ฉันคิดว่าดีที่สุดและแย่ที่สุด

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

ถ้าเราเป็น ไปที่นักพัฒนา จะนำการเปลี่ยนแปลงเหล่านี้ไปใช้มันจะเป็นประโยชน์ต่อชุมชนของเรามากเพราะจะทำให้การเขียนโปรแกรมด้วย Go เป็นที่น่าพอใจยิ่งขึ้น

ในระหว่างนี้หากคุณกำลังพยายามปรับปรุงการทดสอบด้วย Go ให้ลอง การทดสอบแอป Go ของคุณ: เริ่มต้นอย่างถูกวิธี โดยเพื่อน ApeeScapeer Gabriel Aszalos

ที่เกี่ยวข้อง: ลอจิกที่มีโครงสร้างดี: บทช่วยสอน Golang OOP

ทำความเข้าใจพื้นฐาน

Go เป็นภาษาสคริปต์หรือไม่?

มีเส้นบาง ๆ ระหว่างคำจำกัดความของสคริปต์และโปรแกรม แต่ฉันจะบอกว่ามันไม่ใช่ภาษาสคริปต์เนื่องจากโปรแกรม Go ไม่ได้ทำงานในรันไทม์ - พวกมันถูกคอมไพล์และรันเป็นไฟล์ปฏิบัติการ

Go สมบูรณ์แบบหรือไม่?

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

คำถามที่ดีนำไปสู่การออกแบบที่ยอดเยี่ยม: แนวทางในกระบวนการคิดเชิงออกแบบ

การออกแบบ Ux

คำถามที่ดีนำไปสู่การออกแบบที่ยอดเยี่ยม: แนวทางในกระบวนการคิดเชิงออกแบบ
การสร้างแผนความต่อเนื่องทางธุรกิจ

การสร้างแผนความต่อเนื่องทางธุรกิจ

กระบวนการทางการเงิน

โพสต์ยอดนิยม
Nvidia Shield - สิ่งที่แตกต่างบนคอนโซลเกม Android
Nvidia Shield - สิ่งที่แตกต่างบนคอนโซลเกม Android
แผ่นโกงการจัดการโครงการ
แผ่นโกงการจัดการโครงการ
เริ่มต้นใช้งาน Microservices: บทช่วยสอน Dropwizard
เริ่มต้นใช้งาน Microservices: บทช่วยสอน Dropwizard
การแยกการเรียกเก็บเงิน: เรื่องของการเพิ่มประสิทธิภาพ API ภายใน GraphQL
การแยกการเรียกเก็บเงิน: เรื่องของการเพิ่มประสิทธิภาพ API ภายใน GraphQL
กรณีศึกษา: การใช้ ApeeScape เพื่อม้วนปลาใหญ่
กรณีศึกษา: การใช้ ApeeScape เพื่อม้วนปลาใหญ่
 
การประมาณต้นทุนซอฟต์แวร์ในการจัดการโครงการแบบ Agile
การประมาณต้นทุนซอฟต์แวร์ในการจัดการโครงการแบบ Agile
แชทล่ม - เมื่อ Chatbot ล้มเหลว
แชทล่ม - เมื่อ Chatbot ล้มเหลว
ที่ปรึกษาการระดมทุนกับนายหน้า - ตัวแทนจำหน่าย
ที่ปรึกษาการระดมทุนกับนายหน้า - ตัวแทนจำหน่าย
ทำให้ Web Front-end เชื่อถือได้ด้วย Elm
ทำให้ Web Front-end เชื่อถือได้ด้วย Elm
คู่มือสำหรับนักลงทุนเกี่ยวกับน้ำมันปาล์ม
คู่มือสำหรับนักลงทุนเกี่ยวกับน้ำมันปาล์ม
โพสต์ยอดนิยม
  • ฉันควรใช้จาวาสคริปต์เฟรมเวิร์กอะไร
  • วิธีการทำการทดสอบหน่วย
  • webpack รวม node_modules
  • ความแตกต่างระหว่างโหนด js และ javascript
  • c++ ทำงานอย่างไร
หมวดหมู่
  • การจัดการวิศวกรรม
  • Kpi และ Analytics
  • เทคโนโลยี
  • ว่องไว
  • © 2022 | สงวนลิขสิทธิ์

    portaldacalheta.pt