لمرافقة البرنامج ، يجب عليك قراءة الكود ، وكلما كان القيام به أسهل ، كلما بدا الأمر وكأنه لغة طبيعية - ثم سرعان ما انتقلت إلى الموضوع الرئيسي وركزت عليه.
في المادتين الأخيرتين ، أوضحت أن الكلمات المختارة بعناية تساعد على فهم جوهر ما هو مكتوب بشكل أفضل ، لكن التفكير فيها فقط لا يكفي ، لأن كل كلمة موجودة في شكلين: كما في حد ذاتها وكجزء من الجملة. لم يتم تكرار CurrentThread
بعد حتى نقرأه في سياق Thread.CurrentThread
.
وبالتالي ، بالاسترشاد بالملاحظات والألحان البسيطة ، سنرى الآن ماهية الموسيقى.
جدول دورة المحتويات
- الكائنات
- الإجراءات والخصائص
- الرمز كنص
الرمز كنص
تم تصميم معظم الواجهات بطلاقة مع التركيز على الخارج بدلاً من الداخلية ، لذلك فهي سهلة القراءة. بالطبع ، ليس مجانًا: المحتوى ضعيف إلى حد ما. لذلك ، دعنا نقول ، في حزمة FluentAssertions
كتابة: (2 + 2).Should().Be(4, because: "2 + 2 is 4!")
، ونسبة إلى القراءة ، because
تبدو أنيقة ، ولكن داخل Be()
بدلاً من ذلك ، error
أو معلمة errorMessage
.
في رأيي ، هذه الإعفاءات ليست كبيرة. عندما نتفق على أن الكود عبارة عن نص ، فإن مكوناته تتوقف عن الانتماء لأنفسهم: فهي الآن جزء من نوع من "الأثير" العالمي.
سأبين بأمثلة كيف تصبح هذه الاعتبارات تجربة.
Interlocked
اسمحوا لي أن أذكرك بحالة Interlocked
، التي Interlocked.CompareExchange(ref x, newX, oldX)
من Interlocked.CompareExchange(ref x, newX, oldX)
إلى Interlocked.CompareExchange(ref x, newX, oldX)
Atomically.Change(ref x, from: oldX, to: newX)
، باستخدام أسماء واضحة للطرائق والمعلمات.
ExceptWith
اكتب ISet<>
يحتوي على طريقة تسمى ExceptWith
. إذا نظرت إلى مكالمة مثل items.ExceptWith(other)
، فلن تدرك على الفور ما يحدث. لكن عليك فقط أن تكتب: items.Exclude(other)
، لأن كل شيء يقع في مكانه.
GetValueOrDefault
عند العمل مع Nullable<T>
استدعاء x.Value
سيؤدي إلى استثناء إذا كانت x
null
. إذا كنت لا تزال بحاجة إلى الحصول على Value
، استخدم x.GetValueOrDefault
: إنها Value
أو Value
الافتراضية. مرهقة.
يطابق التعبير "أو x أو القيمة الافتراضية" x.OrDefault
القصيرة والأنيقة.
int? x = null; var a = x.GetValueOrDefault();
مع OrDefault
و Or
هناك شيء واحد يستحق التذكر: عند العمل مع مشغل .?
لا يمكنك كتابة شيء مثل x?.IsEnabled.Or(false)
، فقط (x?.IsEnabled).Or(false)
(بمعنى آخر ، يقوم المشغل .?
بإلغاء الجانب الأيمن بأكمله إذا كانت null
في اليسار).
يمكن تطبيق القالب عند العمل مع IEnumerable<T>
:
IEnumerable<int> numbers = null;
Math.Min
و Math.Max
يمكن تطوير فكرة باستخدام Or
إلى أنواع رقمية. افترض أنك تريد أن تأخذ العدد الأقصى من a
و b
. ثم نكتب: Math.Max(a, b)
أو a > b ? a : b
a > b ? a : b
. يبدو كلا الخيارين مألوفًا تمامًا ، لكن لا يبدو أنه لغة طبيعية.
يمكنك استبدالها بـ: a.Or(b).IfLess()
- خذ a
أو b
إذا كان a
أقل . مناسبة لمثل هذه الحالات:
Creature creature = ...; int damage = ...;
string.Join
في بعض الأحيان تحتاج إلى تجميع تسلسل في سلسلة ، مع فصل العناصر بمسافة أو فاصلة. للقيام بذلك ، استخدم string.Join
، على سبيل المثال ، مثل هذا: string.Join(", ", new [] { 1, 2, 3 }); // "1, 2, 3".
string.Join(", ", new [] { 1, 2, 3 }); // "1, 2, 3".
.
قد يصبح "تقسيم رقم الفاصلة" بسيطًا فجأة "أرفق فاصلة لكل رقم من القائمة" - وهذا بالتأكيد ليس رمزًا كنص.
var numbers = new [] { 1, 2, 3 };
Regex
ومع ذلك ، string.Join
غير ضار جدًا مقارنة بكيفية استخدام Regex
أحيانًا بشكل غير صحيح ولأغراض أخرى. حيث يمكنك الحصول على نص بسيط يمكن قراءته ، لسبب ما ، يفضل إدخال معقد للغاية.
لنبدأ بسلسلة بسيطة - تحديد أن السلسلة تمثل مجموعة من الأرقام:
string id = ...;
حالة أخرى هي معرفة ما إذا كان هناك حرف واحد على الأقل في السلسلة من التسلسل:
string text = ...;
كلما كانت المهمة أكثر تعقيدًا ، زاد صعوبة "نمط" الحل: تقسيم سجل من "HelloWorld"
إلى بضع كلمات "Hello World"
، أراد شخص ما بدلاً من خوارزمية بسيطة وجود وحش:
string text = ...;
لا شك أن التعبيرات المنتظمة فعالة وعالمية ، لكنني أريد أن أفهم ما يحدث للوهلة الأولى.
Substring
Remove
يحدث أنك تحتاج إلى إزالة جزء من السطر من البداية أو النهاية ، على سبيل المثال ، من path
- الامتداد .txt
، إن وجد.
string path = ...;
مرة أخرى ، اختفت الحركة والخوارزمية ، وترك سطر بسيط بدون ملحق. exe في النهاية .
نظرًا لأن الأسلوب " Without
يجب أن WithoutExpression
معينة ، فإنهم يتوسلون لآخر: path.Without("_").AtStart
و path.Without("Something").Anywhere
. ومن المثير للاهتمام أيضًا أنه يمكن إنشاء تعبير آخر بنفس الكلمة: name.Without(charAt: 1)
- يحذف الحرف في الفهرس 1 ويعيد سطرًا جديدًا (مفيد في حساب التباديل). وقراءة أيضا!
Type.GetMethods
للحصول على طرق من نوع معين باستخدام الانعكاس ، استخدم:
Type type = ...;
(نفس الشيء ينطبق على GetFields
و GetProperties
.)
Directory.Copy
غالبًا ما يتم تعميم جميع العمليات مع المجلدات والملفات على DirectoryUtils
، FileSystemHelper
. إنهم يقومون بتطبيق نظام الملفات الالتفافية ، التنظيف ، النسخ ، إلخ. ولكن هنا يمكنك التوصل إلى شيء أفضل!
نعرض النص "نسخ جميع الملفات من" D: \ Source "إلى" D: \ Target "" إلى الكود "D:\\Source".AsDirectory().Copy().Files.To("D:\\Target")
. AsDirectory()
- بإرجاع DirectoryInfo
من string
، و Copy()
- ينشئ مثيل لـ CopyExpression
الذي يصف واجهة برمجة تطبيقات فريدة لإنشاء التعبيرات (لا يمكنك استدعاء Copy().Files.Files
، على سبيل المثال). ثم تفتح الفرصة لنسخ ليس كل الملفات ، ولكن بعضها: Copy().Files.Where(x => x.IsNotEmpty)
.
GetOrderById
في المقالة الثانية ، كتبت أن IUsersRepository.GetUser(int id)
لا لزوم لها ، وأفضل ، IUsersRepository.User(int id)
. وفقًا لذلك ، في IOrdersRepository
مماثل IOrdersRepository
لم نحصل على GetOrderById(int id)
، ولكن Order(int id)
. ومع ذلك ، في مثال آخر ، اقترح أن يسمى متغير هذا المستودع ليس _ordersRepository
، ولكن ببساطة _orders
.
كلا التغييرين جيدان من تلقاء نفسه ، لكنهما لا يضيفان معاً في سياق القراءة: استدعاء _orders.Order(id)
يبدو مطوّلًا. قد يكون من الممكن _orders.Get(id)
، لكن الطلبات تفشل ، نريد فقط تحديد الأمر الذي لديه مثل هذا المعرف . واحد هو One
، وبالتالي:
IOrdersRepository orders = ...; int id = ...;
GetOrders
في كائنات مثل IOrdersRepository
، غالبًا ما توجد طرق أخرى: AddOrder
و RemoveOrder
و GetOrders
. يذهب التكرار الأولين ، ويتم الحصول على Add
Remove
(مع الإدخالات المقابلة _orders.Add(order)
و _orders.Remove(order)
). مع GetOrders
الصعب إعادة تسمية Orders
قليلاً. لنرى:
IOrdersRepository orders = ...;
تجدر الإشارة إلى أنه مع _ordersRepository
القديمة _ordersRepository
التكرار في GetOrders
أو GetOrderById
المكالمات ليست ملحوظة للغاية ، لأننا نعمل مع مستودع!
أسماء مثل One
، All
مناسبة للعديد من الواجهات التي تمثل الكثير. لنفترض أن تطبيق جميع مستودعات المستخدم يشبه gitHub.Repository.GetAllForUser("John")
تطبيق معروف لـ GitHub API - octokit
- على الرغم من أنه أكثر منطقية - gitHub.Users.One("John").Repositories.All
. في هذه الحالة ، سيكون الحصول على مستودع واحد ، على التوالي ، gitHub.Repository.Get("John", "Repo")
بدلاً من gitHub.Users.One("John").Repositories.One("Repo")
الواضح gitHub.Users.One("John").Repositories.One("Repo")
. تبدو الحالة الثانية أطول ، ولكنها متسقة داخليًا وتعكس المنصة. بالإضافة إلى ذلك ، باستخدام طرق التمديد ، يمكن اختصارها إلى gitHub.User("John").Repository("Repo")
.
Dictionary.TryGetValue
ينقسم الحصول على القيم من القاموس إلى عدة سيناريوهات تختلف فقط في ما يجب القيام به إذا لم يتم العثور على المفتاح:
- رمي خطأ (
dictionary[key]
) ؛ - إرجاع القيمة الافتراضية (غير
GetValueOrDefault
، ولكن غالبًا ما تكتب GetValueOrDefault
أو TryGetValue
) ؛ - إرجاع شيء آخر (غير مطبق ، ولكن أتوقع
GetValueOrOther
) ؛ - اكتب القيمة المحددة في القاموس وإعادتها (لم يتم تنفيذها ، ولكن
GetOrAdd
).
تتلاقى التعبيرات عند النقطة " تأخذ بعض X أو Y إذا لم تكن X ". بالإضافة إلى ذلك ، كما هو الحال في _ordersRepository
، سوف ندعو متغير القاموس ليس itemsDictionary
، ولكن items
.
ثم بالنسبة إلى الجزء "take some X" ، فإن استدعاء عناصر النموذج. items.One(withKey: X)
مثاليًا ، حيث يُرجع بنية بأربعة نهايات :
Dictionary<int, Item> items = ...; int id = ...;
Assembly.GetTypes
لنلقِ نظرة على إنشاء كافة مثيلات النوع T
الموجودة في التجميع:
وبالتالي ، في بعض الأحيان ، يكون اسم فئة ثابتة هو بداية التعبير.
يمكن العثور على شيء مشابه في NUnit: Assert.That(2 + 2, Is.EqualTo(4))
- Is
لم يتم تصوره كنوع Assert.That(2 + 2, Is.EqualTo(4))
ذاتيًا.
Argument.ThrowIfNull
الآن دعونا نلقي نظرة على الاختيار المسبق:
Ensure.NotNull(argument)
- لطيفة ، ولكن ليس الإنجليزية تماما. شيء آخر هو Ensure(that: x).NotNull()
مكتوب أعلاه. إذا كان هناك فقط يمكن ...
بالمناسبة ، يمكنك! نكتب Contract.Ensure(that: argument).IsNotNull()
واستيراد نوع Contract
using static
. لذا ، يتم الحصول على كل أنواع " Ensure(that: type).Implements<T>()
من Ensure(that: type).Implements<T>()
، Ensure(that: number).InRange(from: 5, to: 10)
، وما إلى ذلك.
فكرة الاستيراد الثابت تفتح العديد من الأبواب. مثال جميل من أجل: بدلاً من items.Remove(x)
اكتب Remove(x, from: items)
. ولكن الغريب هو الحد من enum
والخصائص التي ترجع الوظائف.
IItems items = ...;
Find
الغريبة
في Find(1, @in: items)
C # 7.1 والإصدارات الأحدث ، يمكنك كتابة ليس Find(1, @in: items)
، ولكن Find(1, in items)
، حيث يتم تعريف Find<T>(T item, in IEnumerable<T> items)
أنه Find<T>(T item, in IEnumerable<T> items)
. هذا المثال غير عملي ، لكنه يظهر أن جميع الوسائل جيدة في النضال من أجل القراءة.
في المجموع
في هذا الجزء ، نظرت إلى عدة طرق للعمل مع إمكانية قراءة التعليمات البرمجية. يمكن تعميمها جميعًا على:
- المعلمة المسماة كجزء من التعبير هي
Should().Be(4, because: "")
، Atomically.Change(ref x, from: oldX, to: newX)
. - اسم بسيط بدلاً من التفاصيل الفنية
Separated(with: ", ")
، Exclude
. - الطريقة كجزء من المتغير هي
x.OrDefault()
، x.Or(b).IfLess()
، orders.One(with: id)
، orders.All
. - الطريقة كجزء من التعبير هي
path.Without(".exe").AtEnd
. - النوع كجزء من التعبير هو
Instances.Of
، Is.EqualTo
. - الطريقة كجزء من التعبير (
using static
) هي Ensure(that: x)
، items.All(Weapons)
.
وبالتالي ، يتم وضع الخارجية والمفكر في الصدارة. في البداية يتم التفكير فيه ، وبعد ذلك يتم التفكير في تجسيده المحدد ، وليس مهمًا ، طالما يتم قراءة الرمز كنص. ويترتب على ذلك أن القاضي ليس هو المذاق بقدر اللغة - فهو يحدد الفرق بين item.GetValueOrDefault
و item.OrDefault
.
خاتمة
أيهما أفضل ، أو غير واضحة ، أو غير صالحة للعمل ، أو غير مفهومة؟ قلعة بيضاء الثلج بدون أثاث وغرف أو سقيفة مع أرائك على طراز لويس الرابع عشر؟ يخت فاخر بدون محرك أو بارجة يئن بها كمبيوتر كمومي لا يمكن لأحد استخدامه؟
الإجابات القطبية لا تناسب ، ولكن "في مكان ما في الوسط" أيضا.
في رأيي ، كلا المفهومين لا ينفصمان: باختيار غلاف كتاب بعناية ، فنحن بلا شك ننظر إلى الأخطاء في النص ، والعكس صحيح. لا أريد أن تقوم فرقة البيتلز بتشغيل موسيقى منخفضة الجودة ، ولكن يجب أيضًا أن تسمى MusicHelper .
شيء آخر هو أن العمل على كلمة كجزء من عملية التطوير هو شيء أقل من تقديره ، وهو أمر غير عادي ، وبالتالي لا تزال هناك حاجة إلى نوع من الحكم الشديد. هذه الدورة هي أقصى شكل وصورة.
شكرا لكم جميعا على اهتمامكم!
مراجع
يمكن العثور على أي شخص مهتم برؤية المزيد من الأمثلة على GitHub ، على سبيل المثال ، في مكتبة Pocket.Common
. (ليس للاستخدام العالمي والعالمي)