بناء على صور من gopherize.me
في كثير من الأحيان من كود Go ، يتعين علينا العمل مع العديد من واجهات برمجة تطبيقات HTTP أو العمل كخدمة HTTP بأنفسنا.
واحدة من أكثر الحالات شيوعًا: نتلقى البيانات في شكل هيكل من قاعدة البيانات ، ونرسل الهيكل إلى واجهة برمجة التطبيقات الخارجية ، واستجابة لذلك نتلقى بنية أخرى ، ونحولها بطريقة ما ونحفظها في قاعدة البيانات.
بمعنى آخر: لا تتطلب هذه المعالجة العديد من العمليات المنفصلة مع هياكل الطلب والاستجابة.
من الطبيعي أن يكون لواجهات برمجة التطبيقات حقول في هياكل الطلب والاستجابة يمكن أن تكون صفرية ويمكن أن تأخذ بعض القيم غير الصفرية. عادة ما تبدو مثل هذه الهياكل مثل هذا
type ApiResponse struct {
Code *string json:"code"`
}
ولأنه نوع مرجعي ، يقوم مترجم Go بتحليل الهروب ويمكنه نقل المتغير المحدد إلى الكومة. في حالة الإنشاء المتكرر لمثل هذه المتغيرات ، نحصل على حمل إضافي على GC ويمكننا حتى الحصول على "تسرب للذاكرة" إذا لم يكن لدى GC الوقت لتحرير كل الذاكرة المستخدمة.
ما الذي يمكن عمله في مثل هذه الحالة:
- قم بتعديل واجهة برمجة التطبيقات الخارجية بحيث لا تستخدم قيم صفرية. في بعض الأحيان يكون هذا مقبولًا ، ولكن تغيير واجهة برمجة التطبيقات ليس دائمًا فكرة جيدة: أولاً ، هذا عمل إضافي ، وثانيًا ، الأخطاء التي قد تظهر من إعادة العمل هذه.
- قم بتعديل كود Go الخاص بنا حتى نتمكن من قبول قيم صفرية ، لكن لا تستخدم أنواع المراجع لذلك.
, " "
Go
type pointerSmall struct {
Field000 *string
Field001 *string
Field002 *string
Field003 *string
Field004 *string
Field005 *string
}
,
type valueSmall struct {
Field000 string
Field001 string
Field002 string
Field003 string
Field004 string
Field005 string
}
0 , .
, .
: Go, ( - ) .
— . , . . — . , .. Go .
— , . , .
BenchmarkPointerSmall-8 1000000000 0.295 ns/op 0 B/op 0 allocs/op
BenchmarkValueSmall-8 184702404 6.51 ns/op 0 B/op 0 allocs/op
. , - - .
BenchmarkPointerSmallChain-8 1000000000 0.297 ns/op 0 B/op 0 allocs/op
BenchmarkValueSmallChain-8 59185880 20.3 ns/op 0 B/op 0 allocs/op
JSON . , jsoniter. . , .
BenchmarkPointerSmallJSON-8 49522 23724 ns/op 14122 B/op 28 allocs/op
BenchmarkValueSmallJSON-8 52234 22806 ns/op 14011 B/op 15 allocs/op
, easyjson. , .
BenchmarkPointerSmallEasyJSON-8 64482 17815 ns/op 14591 B/op 21 allocs/op
BenchmarkValueSmallEasyJSON-8 63136 17537 ns/op 14444 B/op 14 allocs/op
: , . (/ ) — .
.
type pointerBig struct {
Field000 *string
...
Field999 *string
}
type valueBig struct {
Field000 string
...
Field999 string
}
. , 0 , ( , .. ). , :
BenchmarkPointerBig-8 36787 32243 ns/op 24192 B/op 1001 allocs/op
BenchmarkValueBig-8 721375 1613 ns/op 0 B/op 0 allocs/op
. . ( , ).
BenchmarkPointerBigChain-8 36607 31709 ns/op 24192 B/op 1001 allocs/op
BenchmarkValueBigChain-8 351693 3216 ns/op 0 B/op 0 allocs/op
.
BenchmarkPointerBigJSON-8 250 4640020 ns/op 5326593 B/op 4024 allocs/op
BenchmarkValueBigJSON-8 270 4289834 ns/op 4110721 B/op 2015 allocs/op
, easyjson. . , jsoniter.
BenchmarkPointerBigEasyJSON-8 364 3204100 ns/op 2357440 B/op 3066 allocs/op
BenchmarkValueBigEasyJSON-8 380 3058639 ns/op 2302248 B/op 1063 allocs/op
: — , . — " ". (easyjson ), — .
— Nullable . sql — sql.NullBool, sql.NullString .
أيضًا ، بالنسبة للنوع ، ستحتاج إلى وصف وظائف التشفير وفك التشفير.
func (n NullString) MarshalJSON() ([]byte, error) {
if !n.Valid {
return []byte("null"), nil
}
return jsoniter.Marshal(n.String)
}
func (n *NullString) UnmarshalJSON(data []byte) error {
if bytes.Equal(data, []byte("null")) {
*n = NullString{}
return nil
}
var res string
err := jsoniter.Unmarshal(data, &res)
if err != nil {
return err
}
*n = NullString{String: res, Valid: true}
return nil
}
كنتيجة للتخلص من أنواع المراجع في API - قمت بتطوير مكتبة نانوية ، مع أنواع Nullable الأساسية مع وظائف التشفير وفك التشفير لـ JSON و jsoniter و easyjson و gocql.
سهولة استخدام أنواع Nullable
وأحد الأسئلة الأخيرة التي يمكنك طرحها حول التبديل إلى أنواع Nullable هو ما إذا كانت ملائمة للاستخدام.
رأيي الشخصي مناسب ، الأنواع لها نفس نمط الاستخدام مثل المراجع المتغيرة.
عند استخدام ارتباط ، نكتب
if a != nil && *a == "sometext" {
مع نوع Nullable ، نكتب
if a.Valid && a.String == "sometext" {