قهرمانان DDD: چشم انداز بودن. من چی هستم؟

🫣 دیدگاه بودن: من چیستم؟
موجودات در Heroes III نام و سطوح خاص خود را دارند.
هر کدام به جناح متفاوتی تعلق دارند، اما موجودات خنثی نیز وجود دارند. برخی از موجودات را می توان ارتقا داد. هر موجودی یک هزینه استخدام تعریف شده و یک نرخ رشد پایه دارد (چند نفر را می توان در هر هفته به خدمت گرفت). موجودات دارای آمار خاصی هستند (مانند حمله، دفاع، امتیاز ضربه و غیره)، که به ویژه در طول مبارزات مهم هستند.
📜 بازی رومیزی انجام می دهید؟ با دستورالعمل شروع کنید!
وقتی روی اسم ها، ساختارهای داده و جداول پایگاه داده برای موجودات تمرکز می کنید، بر اساس این توضیحات چه سوالات روشن کننده ای می توانید بپرسید تا «مدل» بهتری ایجاد کنید؟ و احتمالا چه پاسخ هایی دریافت خواهید کرد؟
- سوال: موجودات چه دارند؟ پاسخ: یک اسم…
- سوال: نام یک موجود چند شخصیت می تواند داشته باشد؟ پاسخ: متغیر است… احتمالاً تا 50 برای جلوگیری از خرابی رابط کاربری.
- سوال: چگونه سطح را تعریف کنیم؟ پاسخ: عددی از 1 تا 7.
- سوال: یک موجود چند ارتقا می تواند داشته باشد؟ پاسخ: از 0 تا 1
از آنجایی که ما با تعریف «چیزی که یک موجود دارد» خیلی خوب عمل کردیم، میتوانیم ادامه دهیم و کل پایگاه داده را ترسیم کنیم، اما ببینیم پادشاه جولین در مورد آن چه میگوید:
https://www.youtube.com/watch?v=wqRnt1uwEjY
تنها مزیت این روش این است که زمان زیادی را برای آن تلف نکردم. ChatGPT کار را به سرعت انجام داد و جداول را به زیبایی ترسیم کرد. احتمالاً به زودی هوش مصنوعی جایگزین چنین برنامه نویسانی خواهد شد. اما شما می خواهید در صنعت بمانید، درست است؟ پس به خواندن ادامه دهید.
شروع یک پروژه با یک طرح داده مانند باز کردن یک بازی تخته است (به هر حال، نسخه Heroes III به تازگی منتشر شده است)، با نگاه کردن به محتویات: “خوب، من 50 کارت دارم و یک تخته دارم”. .. و تلاش برای بازی بدون خواندن دستورالعمل. جای تعجب نیست که با رویکردی مشابه برای برنامه نویسی (بدون درک دامنه و فرآیندهای آن)، ما راه حلی مناسب برای CRUD را در همه جا به کار خواهیم برد – اکثر بازی های رومیزی معمولاً دارای کارت و تخته هستند…
از نمودار جداول پایگاه داده، آیا از قبل می دانید چه کاری باید انجام دهید؟ یا جایی که می توانید کار توسعه دهندگان را موازی کنید؟ آیا تیم های جداگانه می توانند روی آن کار کنند؟ یا چه وابستگی ها یا طراحی های رابط کاربری هنوز وجود ندارد؟ و در نهایت: آیا شما را به درک فرآیندهای تجاری نزدیکتر می کند؟ آیا می دانید چرا موجودات دارای سطوح هستند یا ارتقاء آنها چه اهمیتی دارد؟ ابتدا، بیایید بر درک اصل مسئله تمرکز کنیم. ما به بقیه موارد، مانند جزئیات پایگاه داده، بعداً می پردازیم.
😌 استراحت کن، استراحت کن… من OOP می کنم و کلاس دارم نه میز!
پس چگونه یک موجود «شی گرا» را مدل کنیم؟ در زیر، می توانید پیاده سازی را در Kotlin و شی تجزیه شده به JSON (با مقادیر مثال) را مشاهده کنید.
data class Creature(
val id: String,
val level: Int,
val faction: Faction,
val growth: Int,
val upgrades: Set<Creature>,
val cost: Resources,
val attack: Int,
val defense: Int,
val damage: Range,
val health: Int,
val speed: Int,
val shots: Int = 0,
val size: Int = 1,
val spells: Set<Spell>,
val abilities: Set<SpecialAbility>,
)
{
"id": "Angel",
"level": 7,
"faction": "castle",
"growth": 1,
"upgrades": [
"Archangels"
],
"cost": {
"gold": 3000,
"crystal": 1,
"wood": 0,
"ore": 0,
"sulfur": 0,
"mercury": 0,
"gems": 0
},
"attack": 20,
"defense": 20,
"damage": {
"low": 30,
"high": 50
},
"health": 200,
"speed": 12,
"shots": 0,
"size": 1,
"spells": [],
"abilities": [
{
"type": "HATE",
"creatures": [
"Devil",
"ArchDevil"
]
},
{
"type": "ConstRaisesMorale",
"amount": 1
}
]
}
🔴 پیچیدگی تصادفی و تمامی تست ها قرمز
آیا متغیرهای زیر به خوبی نامگذاری شده اند؟ آره.
آیا همه این صفات متعلق به مخلوق است یا مربوط به اوست؟ آره.
بنابراین، آیا این راه حل مشکلی دارد؟
قبل از اینکه به این سوال پاسخ دهیم، اجازه دهید به دو گفتگو بین یک برنامه نویس و متخصصان دامنه گوش دهیم:
- کارشناس شماره 1: “قهرمان همیشه متعلق به یک بازیکن است.”
- برنامه نویس: “مطمئنی؟ آیا ممکن است موردی وجود داشته باشد که یک قهرمان متعلق به یک بازیکن نباشد؟”
- کارشناس شماره 1: “نه، نه… یک قهرمان روی نقشه همیشه زیر پرچم برخی از بازیکنان است. این هرگز تغییر نخواهد کرد.”
با رضایت، با همکاری متخصصان، مدل کاملی را ایجاد میکنید که الزامات تجاری را برآورده میکند و احساس میکنید که این واقعاً کد پاک است:
data class Hero(
val id: HeroId,
val player: PlayerId
)
بعد از مدتی با متخصص دیگری صحبت می کنید:
- کارشناس شماره 2: “در میخانه، قهرمانی را می خریم که به هیچ بازیکنی تعلق ندارد.” 🤯
- برنامه نویس: “چی؟ اما کارشناس شماره 1 گفت که یک قهرمان همیشه متعلق به یک بازیکن است.”
- کارشناس شماره 2: “او باید اشتباه می کند … من مدت طولانی تری اینجا کار کرده ام و بهتر می دانم.”
در چنین شرایطی چه اتفاقی برای کد شما می افتد؟
شما یک تغییر را معرفی می کنید زیرا کد باید منطق تجاری را منعکس کند.
data class Hero(
val id: HeroId,
val player: PlayerId?, // null only in tavern
val cost: Resources // cost of hiring a hero
)
بنابراین، در نهایت یک تغییر کوچک است – فقط امکان اضافه کردن آن null
در player
رشته. آیا این پایان کار است؟ اصلا! اکنون، تمام تستهایی که یک نمونه Hero ایجاد کردهاند ناموفق هستند و باید بهروزرسانی شوند (به این معنی که دیگر از شما در برابر رگرسیون محافظت نمیکنند). علاوه بر این، به هر جایی که اشاره می کنید hero.player
، باید یک را معرفی کنید if
بیانیه برای بررسی در برابر null.
اگر از کاتلین (یا زبان دیگری با ایمنی تهی پیچیده) استفاده میکنید و کامپایلر به شما هشدار میدهد، به جای ایجاد اشکال در تولید، همچنان قابل مدیریت است. حتی اگر از عهده این کار بر بیایید، حالا می خواهید تغییرات و… بم (به قول پسر 1 ساله ام) ادغام شود! به نظر می رسد توسعه دهنده دیگری قبلاً تغییرات شما را بازنویسی کرده است، و ما یک تضاد حاشیه داریم! وقتی جناح های مختلف را با هم ترکیب می کنید، روحیه و بازده کاری شما مانند ارتش قهرمان در Heroes III کاهش می یابد. آیا سادهتر نیست، بهجای اصلاح، بهکارگیری اصل بسته باز در سطح معماری و داشتن دو مدل مجزا؟ مدل هایی که حتی تیم های جداگانه ای از توسعه دهندگان می توانند بدون هیچ مشکلی روی آنها کار کنند. بیایید یک مثال را در زیر ببینیم.
🐉 عادات بد – اینجا اژدها باشید!
ویژگیهای متفاوتی برای توصیف یک قهرمان در بافت میخانه در مقایسه با هنگام کاوش در نقشه ماجراجویی مورد نیاز است. به سادگی فضای نام/ماژول دیگری یا هر چیزی که در محیط خود نامیده می شود اضافه کنید و دو کلاس جداگانه ایجاد کنید. اینجا هیچ چیز شما را محدود نمی کند. مگر اینکه، البته، شما هنوز در مورد آن فکر می کنید Hero
جدول و اضافه کردن یک ستون nullable یا روابط بین جداول. از ابتدا به ما این گونه آموزش داده شد و مبارزه با عادت های بد سخت ترین کار است. این عادتها مانند اژدهای خطرناکی هستند، اما اگر آنها را شکست دهید – شما و پروژهتان پاداشهای بیشتری نسبت به شکست دادن یک آرمانشهر اژدها دریافت خواهید کرد.
هنگامی که اطلاعات متناقضی از متخصصان در مورد یک اسم می شنوید، تلاش برای تطبیق آنها در یک مدل واحد، چیزی را معرفی می کند که به عنوان پیچیدگی تصادفی شناخته می شود. حالا، هر برنامه نویسی، حتی کسی که تقریباً کل سیستم را می شناسد (اما نه میخانه و استخدام قهرمان)، از شما می پرسد: “رفیق، چرا باید اینجا را برای null بررسی کنم؟” و این کم خطرترین شکل این مشکل است… هر دوی شما فقط زمان خود را تلف خواهید کرد.
در نتیجه، چنین مشکلی فقط در کد وجود دارد و درک خود دامنه را سختتر از آنچه هست میکند. آیا یکی از کارشناسان به شما دروغ می گوید یا ناتوان؟ اصلا! در میخانه، می توانید یک قهرمان بخرید، اما نمی توانید هیچ یک از هیروهایی که روی نقشه حرکت می کنند، بخرید، زیرا آنها متعلق به شما یا بازیکن دیگری هستند.
چنین ناهماهنگی هایی در عبارات “تجاری” نشانه روشنی است که اکنون باید در مورد رفتارها صحبت کنیم تا با اسم ها جلو برویم. اسم “قهرمان” یکسان است، اما رفتارها – عملیاتی که می توان روی شی انجام داد – کاملا متفاوت است، بستگی به زمینه دارد.
اگر می خواهید فعالانه شرکت کنید و قسمت های بعدی را از دست ندهید، بیایید در لیست پستی من در اینجا ثبت نام کنید.