io.Reader و io.Writer
جزء من قسم البرمجة كائنية التوجه في رحلة GO على Coddy. الدرس 73 من 107.
تُبرز المكتبة القياسية في Go التصميم القائم على الواجهات من خلال واجهتين أساسيتين: io.Reader وio.Writer. وتُشغّل هاتان الواجهتان البسيطتان كل شيء بدءًا من عمليات الملفات ووصولًا إلى الاتصالات الشبكية.
تتطلب الواجهة io.Reader طريقة واحدة فقط:
type Reader interface {
Read(p []byte) (n int, err error)
}يقرأ ما يصل إلى len(p) من bytes في الشريحة ويُرجع عدد bytes التي تمت قراءتها. وبالمثل، يعرّف io.Writer ما يلي:
type Writer interface {
Write(p []byte) (n int, err error)
}أي نوع implementing هذه الأساليب يعمل مع منظومة I/O بأكملها.
إليك نوعًا مخصصًا يطبّق io.Reader:
type RepeatReader struct {
Char byte
Count int
pos int
}
func (r *RepeatReader) Read(p []byte) (int, error) {
if r.pos >= r.Count {
return 0, io.EOF
}
n := 0
for n < len(p) && r.pos < r.Count {
p[n] = r.Char
n++
r.pos++
}
return n, nil
}يعمل هذا القارئ المخصّص الآن مع أي function تتوقع io.Reader:
reader := &RepeatReader{Char: 'A', Count: 5}
data, _ := io.ReadAll(reader)
fmt.Println(string(data)) // AAAAAيتيح لك هذا التصميم كتابة دوال تقبل io.Reader أو io.Writer، مما يجعلها تعمل مع الملفات أو اتصالات الشبكة أو المخازن المؤقتة أو أي تطبيق مخصص.
التحدي
سهللنَبْنِ معالجًا مخصصًا لتدفق النصوص ينفّذ الواجهتين io.Reader وio.Writer! ستنشئ أنواعًا يمكنها تحويل النص أثناء مروره خلالها، موضحًا كيف تتيح هذه الواجهات الأساسية تركيبًا قويًا لعمليات الإدخال/الإخراج.
ستنظّم شيفرتك عبر ملفين:
streams.go: عرّف أنواع القارئ والكاتب المخصصة لديك.أنشئ بنية
CountingReaderتغلّف سلسلة نصية وتتتبّع عدد bytes التي تمت قراءتها. يجب أن تحتوي على حقول للسلسلة المصدر ومتتبّع position. نفّذ الأسلوبRead(p []byte) (int, error)الذي يقرأ bytes من المصدر إلى الشريحة المقدّمة، ويحدّث position، ويعيدio.EOFعند استنفاد المصدر.أنشئ بنية
UppercaseWriterتجمع data المكتوبة وتحولها إلى أحرف كبيرة. يجب أن تخزّن النتيجة المتراكمة داخليًا. نفّذ الأسلوبWrite(p []byte) (int, error)الذي يحوّل bytes الواردة إلى أحرف كبيرة ويخزّنها. أضف أسلوبResult() stringلاسترداد النص المتراكم بالأحرف الكبيرة.أنشئ دوال المُنشئ
NewCountingReader(source string) *CountingReaderوNewUppercaseWriter() *UppercaseWriterلتهيئة أنواعك بصورة صحيحة.main.go: اعرض عمل أنواع الإدخال/الإخراج المخصصة لديك مع المكتبة القياسية.اقرأ سلسلة نصية input، ثم استخدم
CountingReaderمعio.ReadAllلقراءة كل data. مرّر تلك data عبرUppercaseWriterباستخدام أسلوبWriteالخاص به.اطبع النتائج بهذا التنسيق:
Original: [input] Uppercase: [result from writer] Bytes read: [total bytes]
سيتم توفير input التالي:
- سطر واحد من النص
على سبيل المثال، given:
Hello Worldيجب أن يكون الناتج:
Original: Hello World
Uppercase: HELLO WORLD
Bytes read: 11وبالنسبة إلى:
Go interfaces are powerfulيجب أن يكون الناتج:
Original: Go interfaces are powerful
Uppercase: GO INTERFACES ARE POWERFUL
Bytes read: 26الفكرة الأساسية هي أن CountingReader يعمل بسلاسة مع io.ReadAll لأنه implements io.Reader. ستعمل أي function في منظومة Go تقبل io.Reader مع نوعك المخصص: تكمن قوة التصميم القائم على interface في ذلك.
جرّب بنفسك
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// اقرأ الإدخال
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
// أزل السطر الجديد اللاحق إن وُجد
if len(input) > 0 && input[len(input)-1] == '\n' {
input = input[:len(input)-1]
}
// TODO: أنشئ CountingReader باستخدام سلسلة الإدخال
// countingReader := NewCountingReader(input)
// TODO: استخدم io.ReadAll لقراءة جميع البيانات من CountingReader الخاص بك
// data, err := io.ReadAll(countingReader)
// TODO: أنشئ UppercaseWriter
// upperWriter := NewUppercaseWriter()
// TODO: اكتب البيانات إلى UppercaseWriter الخاص بك
// upperWriter.Write(data)
// TODO: اطبع النتائج بالتنسيق المطلوب:
// Original: [input]
// الأحرف الكبيرة: [النتيجة من الكاتب]
// البايتات المقروءة: [إجمالي البايتات]
// عنصر نائب لتجنب خطأ الاستيراد غير المستخدم - أزله عند التنفيذ
_ = io.EOF
fmt.Println("Original:", input)
// fmt.Println("Uppercase:", upperWriter.Result())
// fmt.Println("Bytes read:", countingReader.Position)
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP في Go
الملفات الخارجيةمساحة العمل والـ Modules في Goالـ Packages والـ Importsالأسماء المصدرة مقابل غير المصدرةمقدمة إلى الـ OOP في Goالـ Structs كـ Classesتعريف الـ Methods في الـ Structsالـ Pointer Receivers مقابل الـ Value Receiversتهيئة الـ Structدوال الـ Constructorمراجعة - آلة حاسبة بسيطة4الواجهات
مقدمة في الواجهاتالتنفيذ الضمنيالواجهة كعقدالواجهة الفارغة (any)تأكيد النوع (Type Assertion)تبديل النوع (Type Switch)تركيب الواجهاتواجهات Stringer و Errorمراجعة - حاسبة الأشكال7التغليف
الحقول المصدرة وغير المصدرةالتغليف على مستوى الحزمةدوال الـ Getter والـ Setterإخفاء المعلومات في Goملخص - سجلات الطلاب10الأنواع العامة (Go 1.18+)
مقدمة في الأنواع العامةمعاملات الأنواعقيود الأنواعالـ Structs العامةحل بديل للـ Methods العامةملخص - المجموعات العامة13أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Abstract Factoryنمط Observerنمط Strategy2تعمق في الأنواع و Structs
الأنواع الأساسية والمركبةتعريفات الأنواع المخصصةStruct TagsStructs مجهولةStructs متداخلةالقيم الصفرية والافتراضيةمراجعة - دفتر العناوين5التركيب بدلاً من الوراثة
لماذا لا تدعم Go الوراثةأساسيات تضمين الـ Structترقية الـ Methodتضمين عدة Structsالتضمين مقابل التجميعحجب الـ Methods المضمنةملخص - الهيكل الهرمي للموظفين8معالجة الأخطاء و OOP
واجهة errorأنواع الأخطاء المخصصةتغليف الأخطاء (fmt.Errorf)أخطاء Sentinelerrors.Is() و errors.As()Panic و Defer و Recoverملخص - File Parser11المكتبة القياسية والبرمجة كائنية التوجه (OOP)
io.Reader و io.Writerواجهة sort.Interfaceواجهة fmt.Stringerencoding/json مع Structsواجهة http.Handlerمراجعة - نماذج REST API14أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبMiddleware كنمط مزين3المؤشرات والذاكرة
أساسيات المؤشرات في Goالمؤشرات إلى الـ Structsالتمرير بالقيمة مقابل التمرير بالمرجعدالة ()newGarbage Collection في Goملخص - بناء Linked List6تعدد الأشكال في Go
تعدد الأشكال عبر InterfacesDuck Typing في Goقواعد استيفاء الـ Interfaceمجموعات متعددة الأشكالحقن التبعيةمراجعة - معالج الدفع9التزامن والبرمجة كائنية التوجه (OOP)
أساسيات الـ Goroutinesالقنوات (Channels) والاتصالالقنوات المخزنة (Buffered) مقابل غير المخزنةجملة Selectsync.Mutex و sync.RWMutexsync.WaitGroupتصميم الـ Structs الآمنة للخيوط (Thread-Safe)مراجعة - Worker Poolتدرّب بنفسك: مترجم Go عبر الإنترنت