<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>&#8235;try, catch, write - Ron Klein &#187; תכנות&#8236;</title>	<atom:link href="http://heblog.ronklein.co.il/category/%d7%aa%d7%9b%d7%a0%d7%95%d7%aa/feed/" rel="self" type="application/rss+xml" />
	<link>http://heblog.ronklein.co.il</link>
	<description>&#8235;הבלוג של רון קליין: תכנות, OOP, טכנולוגיה, Design Patterns, log4net ועוד עניינים&#8236;</description>	<lastBuildDate>Wed, 14 Jul 2010 23:23:11 +0000</lastBuildDate>
	<language>he</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>&#8235;Toolbox: A Decent Text Editor&#8236;</title>		<link>http://heblog.ronklein.co.il/2010/07/toolbox-a-decent-text-editor/</link>
		<comments>http://heblog.ronklein.co.il/2010/07/toolbox-a-decent-text-editor/#comments</comments>
		<pubDate>Wed, 14 Jul 2010 23:23:11 +0000</pubDate>
		<dc:creator>&#8235;רון קליין&#8236;</dc:creator>				<category><![CDATA[תכנות]]></category>
		<category><![CDATA[Text-Editor]]></category>
		<category><![CDATA[Toolbox]]></category>

		<guid isPermaLink="false">http://heblog.ronklein.co.il/?p=597</guid>
		<description><![CDATA[&#8235;והפעם, הפרק הראשון בסדרת &#34;מה כדאי שיהיה לכל מתכנת&#34;. ההמלצה הראשונה שלי היא עורך טקסט נורמלי. משתמשי Windows מכירים את ה Notepad (סליחה, &#34;פנקס הרשימות&#34;). זה בהחלט עורך טקסט, אבל לא מהמשובחים (למרות שבענייני BiDi הוא מעולה). אין לו טאבים, למשל. והוא גם לא יודע להציג XML עם צבעים (Syntax highlighting) כמו הויז'ואל סטודיו. למה [...]&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p>והפעם, הפרק הראשון בסדרת &quot;מה כדאי שיהיה לכל מתכנת&quot;.</p>
<p>ההמלצה הראשונה שלי היא עורך טקסט נורמלי. משתמשי Windows מכירים את ה Notepad (סליחה, &quot;פנקס הרשימות&quot;). זה בהחלט עורך טקסט, אבל לא מהמשובחים (למרות שבענייני BiDi הוא מעולה). אין לו טאבים, למשל. והוא גם לא יודע להציג XML עם צבעים (<a title="Wikipedia - Syntax highlighting" href="http://en.wikipedia.org/wiki/Syntax_highlighting" target="_blank">Syntax highlighting</a>) כמו הויז'ואל סטודיו.</p>
<p>למה בכלל צריך עורך טקסט? האמת, לא חייבים, הרי יש ויז'ואל סטודיו. אבל הוא כל כך כבד ומגושם, שעד שהוא עולה לוקח הרבה זמן. מה גם שההתקנה שלו לא טריויאלית. אז אם רק צריך לעדכן איזה web.config על המחשב (ואולי אפילו על שרת בדיקות, שאין עליו בכלל VS) &#8211; אנחנו נרצה עורך טקסט, ואם אפשר כזה שמגיע עם פונקציונאליות מתקדמת כמו:</p>
<ul>
<li>טאבים (או כל פטנט אחר שמאפשר לצפות במספר קבצים במקביל בצורה נוחה)</li>
<li>צביעת טקסט &#8211; Syntax highlighting</li>
<li>סימון השורה הפעילה</li>
<li>חיפוש מתקדם של טקסט בתוך הקובץ/הקבצים הפעילים/בתיקיה מסויימת</li>
<li>וכו'</li>
</ul>
<p>והכי חשוב &#8211; <strong>טעינה מהירה</strong> (של העורך עצמו ושל הקובץ שרוצים לערוך).</p>
<p>יש די הרבה עורכי טקסט טובים:</p>
<ul>
<li><span style="unicode-bidi: bidi-override; direction: ltr ! important;">Notepad++</span></li>
<li>Notepad2</li>
<li>UltraEdit</li>
<li>TextPad</li>
<li>EditPad</li>
<li>EditPlus</li>
</ul>
<p>ועוד ועוד. האמת, למה לי לחזור על מילים רבות שכבר נכתבו? הנה <a title="What are the best alternatives to Notepad? - Stackoverflow" href="http://stackoverflow.com/questions/34215/what-are-the-best-alternatives-to-notepad" target="_blank">סקירה של עורכי טקסט ב stackoverflow</a>, ואפשר גם <a title="Best Alternative to Notepad - Google" href="http://www.google.co.il/search?q=best+alternative+to+notepad" target="_blank">לגגל</a>, כמובן.</p>
<p>אני אישית בוחר ב <span style="unicode-bidi: bidi-override; direction: ltr ! important;">Notepad++</span> בתור העורך &quot;שלי&quot;. לא שעשיתי סקירה מקיפה, פשוט למדתי לעבוד איתו, והוא מספק את הסחורה. יש לו גם פיצ'ר חביב שבקליק ימני על קובץ יש לי אפשרות לערוך איתו את הקובץ:</p>
<div id="attachment_599" class="wp-caption alignnone" style="width: 601px"><img class="size-full wp-image-599" title="קליק ימני" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/07/right-click.png" alt="" width="591" height="390" /><p class="wp-caption-text">קליק ימני על קובץ מאפשר לערוך אותו עם NPP</p></div>
<p>עוד יתרון ב NPP (שמו השני של <span style="unicode-bidi: bidi-override; direction: ltr ! important;">Notepad++</span>) הוא מערכת plug-ins מובנית, שמאפשרת להרחיב את הפונקציונליות שלו. כך למשל אפשר להתקין plug-in שיהיה hex-editor, ויש גם plug-in להשוואה בין קבצים.<br />
מהנסיון הלא מועט שלי עם NPP יקירי, יש לי רק תלונה אחת: יש לו בעיה ב find/replace. לא משהו ברור, אבל יש חריקות. לא נורא, הוא עדיין הבחירה שלי. את הסריקות אפשר לעשות עם <a title="Advanced Find and Replace" href="http://www.abacre.com/afr/" target="_blank">Advanced Find and Replace</a> &#8211; תוכנה יעודית וסופר-מהירה לעניין זה.<br />
אה כן, זה freeware לגמרי, open-source ברשיון GPL. בקיצור, זה אחלה (וזה גדול).</p>
<p>נו, הנה לינק <a title="NPP - Home Page" href="http://notepad-plus-plus.org" target="_blank">לדף הבית שלהם</a>. חלאס, מה הלחץ?!</p>
</div>]]></content:encoded>			<wfw:commentRss>http://heblog.ronklein.co.il/2010/07/toolbox-a-decent-text-editor/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8235;Tip: Just Run It&#8236;</title>		<link>http://heblog.ronklein.co.il/2010/07/tip-just-run-it/</link>
		<comments>http://heblog.ronklein.co.il/2010/07/tip-just-run-it/#comments</comments>
		<pubDate>Fri, 02 Jul 2010 07:01:35 +0000</pubDate>
		<dc:creator>&#8235;רון קליין&#8236;</dc:creator>				<category><![CDATA[תכנות]]></category>
		<category><![CDATA[tip]]></category>
		<category><![CDATA[Visual Studio]]></category>

		<guid isPermaLink="false">http://heblog.ronklein.co.il/?p=576</guid>
		<description><![CDATA[&#8235;לפעמים אני רק רוצה לבדוק משהו קטן בקוד ולהריץ. קוד קצר, בלי עניינים מיותרים. אז אני פותח את הויז'ואל סטודיו, ובוחר בפרוייקט מסוג Console Application, הבחירה הטבעית לעניין שכזה. אבל Visual Studio 2010, כברירת מחדל, מחייב אותי לשמור את הפרוייקט בתיקיה, ורק אז אני אוכל להתחיל לעבוד. זה יכול להיות קצת מציק, כי אני רק [...]&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p>לפעמים אני רק רוצה לבדוק משהו קטן בקוד ולהריץ. קוד קצר, בלי עניינים מיותרים. אז אני פותח את הויז'ואל סטודיו, ובוחר בפרוייקט מסוג Console Application, הבחירה הטבעית לעניין שכזה.</p>
<p>אבל Visual Studio 2010, כברירת מחדל, מחייב אותי לשמור את הפרוייקט בתיקיה, ורק אז אני אוכל להתחיל לעבוד. זה יכול להיות קצת מציק, כי אני רק רוצה לבדוק משהו בקטנה, לא להרים פרויקט פורמלי עם קבצים ותיקיית bin\debug.</p>
<p>כאן באה לעזרתנו הגדרה גלובלית ב Visual Studio 2010: מתפריט <em>Tools</em> בוחרים ב <em>Options</em>, ובחלון שנפתח בוחרים ב <em>Projects and Solutions</em>, ושם יש את האופציה הבאה:<br />
Save new projects when created<br />
כפי שרואים בתמונה כאן, האופציה הזו מסומנת (checked) כברירת מחדל:</p>
<p><img class="alignnone size-full wp-image-578" title="Visual Studio Options" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/07/vs-default-options.png" alt="" width="474" height="333" /></p>
<p>אפשר להסיר את הסימון מהאופציה הזו, ואז חלק מהפרויקטים שתבחרו (כולל Console Application) &#8211; יפתחו מבלי שנצטרך לבחור תיקיה לשמירה וכו'. נוכל גם להריץ, לדבג והכל. בסיום העבודה (סגירת הויז'ואל או סגירת ה solution) נקבל את הדיאלוג הבא:</p>
<p><img class="alignnone size-full wp-image-579" title="Close Solution Dialog" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/07/save-discard-cancel.gif" alt="" width="419" height="179" /></p>
<p>אם נבחר באופציה Save &#8211; נוכל, כמובן, לשמור את ה solution שלנו.</p>
<p>אבל אם נבחר באופציה Discard &#8211; הפרויקט הנ&quot;ל <strong>ימחק לחלוטין</strong>, כולל תיקיות bin\debug! יש!!<br />
(למעשה כל הפרויקט נשמר ב <span style="unicode-bidi: bidi-override; direction: ltr ! important;">&lt;current-user&gt;\AppData\Local\Temporary Projects</span> ומשם הוא נמחק)</p>
<p>נייס! <img src='http://heblog.ronklein.co.il/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
</div>]]></content:encoded>			<wfw:commentRss>http://heblog.ronklein.co.il/2010/07/tip-just-run-it/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8235;Web Caching Techniques&#8236;</title>		<link>http://heblog.ronklein.co.il/2010/06/web-caching-techniques/</link>
		<comments>http://heblog.ronklein.co.il/2010/06/web-caching-techniques/#comments</comments>
		<pubDate>Wed, 30 Jun 2010 00:02:08 +0000</pubDate>
		<dc:creator>&#8235;רון קליין&#8236;</dc:creator>				<category><![CDATA[תכנות]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[Cache]]></category>
		<category><![CDATA[Global-Variables]]></category>

		<guid isPermaLink="false">http://heblog.ronklein.co.il/?p=539</guid>
		<description><![CDATA[&#8235;בפוסט הזה: כמה מילים על cache טכניקות נפוצות לשימוש ב cache ב web: - האובייקט Cache - האובייקט Application - עבודה עם static properties סיכום ועוד כמה מילים הקדמה &#8211; כמה מילים על Cache עולם המיחשוב הוא עולם שמבוסס, בסופו של דבר, על מספרים. כל הנתונים שנשמרים במחשב מיוצגים, בסופו של דבר, ע&#34;י מספרים: תמונות [...]&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p>בפוסט הזה:</p>
<ul>
<li>כמה מילים על cache</li>
<li>טכניקות נפוצות לשימוש ב cache ב web:<br />
- האובייקט Cache<br />
- האובייקט Application<br />
- עבודה עם static properties</li>
<li>סיכום ועוד כמה מילים</li>
</ul>
<h1>הקדמה &#8211; כמה מילים על Cache</h1>
<div id="attachment_544" class="wp-caption alignleft" style="width: 229px"><a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/06/take_a_number.jpg"><img class="size-full wp-image-544" title="קח מספר" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/06/take_a_number.jpg" alt="" width="219" height="188" /></a><p class="wp-caption-text">קח מספר</p></div>
<p>עולם המיחשוב הוא עולם שמבוסס, בסופו של דבר, על <strong>מספרים</strong>. כל הנתונים שנשמרים במחשב מיוצגים, בסופו של דבר, ע&quot;י מספרים: תמונות הן רצף של מספרים, טקסט הוא רצף של מספרים, וכו' וכו'. במילים אחרות, מידע <strong>דיגיטלי</strong> (digital) הוא מידע <strong>ספרתי</strong>, כלומר מבוסס על ספרות (digits).</p>
<p>למה אני כותב את כל זה? כי למספרים (ולרצפים של מספרים) יש תכונה מעניינת: <strong>אפשר לשכפל אותם</strong>. יום-יום אני מעתיק קבצים מהמחשב לדיסק-און-קי שלי, וזה אפשרי בגלל שקובץ הוא רצף של מספרים, שניתן לשכפל אותו בקלות. התכונה הזו של שכפול מספרים מאפשרת לנו להשתמש (או לבנות) מערכת של Caching. לפני שנגדיר מה זה מערכת של Caching, בואו נבין מה <strong>הצורך</strong>.</p>
<p>אז ככה: נניח שיש לנו אפליקציית Web (או למעשה כל אפליקציית Server, אבל Web זו דוגמה נפוצה וברורה), ויש לה הרבה משתמשים בו-זמנית. נניח שזו אפליקציה של מדריך טלפון, שבה המשתמש יכול לחפש טלפון לפי שם פרטי, שם משפחה, וישוב (בישראל). אם כך, כל משתמש רואה ב browser שלו את רשימת הישובים.</p>
<p>בואו נניח גם, שבצד השרת, טעינת רשימת הישובים היא פעולה יחסית ארוכה.</p>
<p>ובואו נניח עוד משהו, שרשימת הישובים מתעדכנת לעיתים רחוקות.</p>
<p>אם אנחנו עובדים בלי Cache, אז כל בקשה מכל משתמש שמבקר באתר שלנו &#8211; טוענת את רשימת הישובים, ולכן לוקחת די הרבה זמן (נניח שזה יוצא 3 שניות המתנה, וזה המון למשתמש).</p>
<p>ה<strong>צורך</strong> שלנו, אם כן, הוא לקצר את זמן ההמתנה של המשתמש כדי שהאתר שלנו יהיה מהיר ונוח. בקיצור, אנחנו רוצים <strong>מהירות</strong>.</p>
<p>עבודה עם Cache עונה על הצורך של מהירות, ע&quot;י <strong>אחסון עותק של הנתונים בזכרון</strong> (בזכרון ה RAM של השרת): שליפת נתונים מהזכרון היא מאוד מהירה יחסית לשליפת נתונים מקובץ בדיסק או ממקור חיצוני אחר (כמו מסד נתונים, Web-Service וכו'). מצד שני, הזכרון הזמין בצד השרת הוא מוגבל בגודלו. לכן לא נוכל לאחסן את כל הנתונים שם.</p>
<div id="attachment_546" class="wp-caption alignleft" style="width: 260px"><a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/06/cash.jpg"><img class="size-full wp-image-546" title="cash" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/06/cash.jpg" alt="" width="250" height="188" /></a><p class="wp-caption-text">נשמע אותו דבר</p></div>
<p>בקיצור, Cache (ובעברית &quot;מטמון&quot;) זו פעולה של אחסון נתונים בהתקן מסויים, כך ששליפתם מההתקן הזה תהיה מהירה יותר משליפת הנתונים בצורה המקורית. בדוגמה הקודמת, ההתקן הזה היה זכרון ה RAM של השרת. לפעמים מאחסנים את הנתונים בדיסק מהיר יותר, וגם זה יכול להיות cache. לפעמים מאחסנים את הנתונים ב Flash-Disk, וגם זה יכול להיות cache.<br />
בכל מקרה, מנגנון של Caching יכול גם לבדוק את צריכת הזכרון ע&quot;י הנתונים שמאוחסנים בו, ומדי פעם לנקות חלק מהזכרון לפי חוקים מסויימים. יש מגוון רחב של אלגוריתמים לניהול Cache, ולפרטים נוספים מומלץ לקרוא <a title="ויקיפדיה - caching algorithms" href="http://en.wikipedia.org/wiki/Cache_algorithms" target="_blank">בויקיפדיה</a>.</p>
<p>בפוסט הזה אני אתמקד ב Caching של אפליקציית Web שכתובה ב ASP.NET, אבל בשורה התחתונה זה יכול להיות לכל שירות כמו WCF שהוא self-hosted.</p>
<h1>פתרונות Caching קיימים</h1>
<p>באפליקציית Web שכתובה ב ASP.NET יש מספר טכניקות ל Caching של מידע גולמי (כלומר לא HTML) בזכרון השרת:</p>
<ul>
<li>האובייקט Cache</li>
<li>האובייקט Application</li>
<li>משתנים גלובליים</li>
</ul>
<h2>System.Web.Caching.Cache</h2>
<p>האובייקט Cache (מתוך System.Web.Caching) מכיל את האפשרויות העשירות ביותר במתודה Insert: לצד ה key וה value (הנתונים עצמם):</p>
<ul>
<li>אפשר לציין שהנתונים תלויים במקור חיצוני (שאפשר לדגום אותו), כמו קובץ או טבלה ב DB</li>
<li>אפשר לקבוע שהנתונים ישארו ב cache רק עד תאריך/שעה מסויימים (absolute expiration)</li>
<li>אפשר לקבוע שהנתונים ישארו ב cache כל עוד נעשה בהם שימוש בחלון זמן מסויים (sliding expiration)</li>
<li>אפשר לקבוע קדימויות של הפריטים שנשמרים ב cache, כך שאם, למשל, צריך להוציא פריט אחד מה cache, ויש שני פריטים &quot;מועמדים להדחה&quot;, אז הפריט שישאר ב cache הוא זה עם הקדימות הגבוהה יותר.</li>
<li>ואפשר לדעת בדיוק מתי כל פריט יוצא מה cache</li>
</ul>
<p>למעשה, כדי לבצע caching בצורה טובה, אפשר וכדאי לסמוך על האובייקט Cache, כי יש לו גישה בלעדית למשאבי השרת והוא יכול לחשב את העומס על הזכרון המוקצה לאפליקציה שלנו, ולפיכך להוציא פריטים מה cache כשצריך (הוא בודק מדי פעם את צריכת הזכרון).</p>
<p>עם זאת, האובייקט Cache יכול להיות קצת מגושם: הנתונים הנשלפים הם רק מסוג object, ולכן צריך להמיר אותם ל type הרצוי. מעבר לכך, מכיוון שהוא זמין בכל האפליקציה, כל הכנסה והוצאה של פריטים גורמת לנעילה ולבדיקות פנימיות.</p>
<h2>משתנים גלובליים &#8211; האובייקט Application</h2>
<p>האלטרנטיבה ל Cache הוא משתנים גלובליים. פעם, בתקופת ASP3 ומטה, מה שנקרא <a title="ויקיפדיה - Classic ASP" href="http://en.wikipedia.org/wiki/Active_Server_Pages" target="_blank">Classic ASP</a>, לא היה לנו אובייקט Cache כל כך חכם, וכדי לאחסן נתונים בזכרון היה לנו רק את האובייקט Application, שהיה רק אוסף של key/value pairs. בקיצור, היה רק dictionary, בלי היכולת לחשב צריכת זכרון, קדימויות, תוקף וכו' לפריטים השונים. כנראה כדי להיות תואמים לאחור, מיקרוסופט הוציאו את ASP.NET עם אותו אובייקט Application שהוא אובייקט מטיפוס HttpApplicationState. כדי להשתמש באובייקט Application נרשום קוד בצורה הזו:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw1">partial</span> <span class="kw4">class</span> MyPage <span class="sy0">:</span> <span class="kw5">System.<span class="me1">Web</span>.<span class="me1">UI</span></span>.<span class="me1">Page</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">protected</span> <span class="kw1">void</span> Page_Load<span class="br0">&#40;</span><span class="kw4">object</span> sender, EventArgs e<span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co1">// get a value</span><br />
&nbsp; &nbsp; <span class="kw4">int</span> diamond <span class="sy0">=</span> <span class="br0">&#40;</span><span class="kw4">int</span><span class="br0">&#41;</span> Application<span class="br0">&#91;</span><span class="st0">&quot;Wish&quot;</span><span class="br0">&#93;</span><span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="co1">// set a value</span><br />
&nbsp; &nbsp; Application<span class="br0">&#91;</span><span class="st0">&quot;Wall&quot;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;numb&quot;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<div id="attachment_549" class="wp-caption alignleft" style="width: 201px"><a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/06/looking_for_cache1.jpg"><img class="size-full wp-image-549" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/06/looking_for_cache1.jpg" alt="" width="191" height="226" /></a><p class="wp-caption-text">caching בתקופות קדומות</p></div>
<p>היתרון של האובייקט Application הוא שהפריטים המאוחסנים בו &#8211; נשארים שם, אלא אם כן היתה קריאה מפורשת למתודת Remove. אגב, אפשר להגיע לתוצאות דומות אם נשים פריט באובייקט Cache ונציין NotRemovable ב CacheItemPriority.</p>
<p>החסרון של האובייקט Application הוא שעדיין נצטרך לעשות casting כשנרצה לשלוף ממנו נתונים. ושוב, בדומה ל Cache, יש בו מגנון פנימי של נעילות (כי הנתונים נשמרים במבנה אחד ויחיד שמשותף לכל האפליקציה).</p>
<p>חסרון נוסף הוא המימוש של האובייקט הזה: לפחות לפי הפריימוורק שמותקן אצלי במחשב, המימוש הוא קצת מיושן ובעייתי. יש שם שימוש בטיפוס Hashtable (קצת מיושן, אבל מילא) ויש שם קוד עם נעילות מהסוג של <span style="unicode-bidi: bidi-override; direction: ltr ! important;">lock(this)</span> וזה, איך לומר במילים עדינות, לא להיט. מי שלא יודע למה זה לא להיט <a title="stackoverflow - Why is lock(this) {…} bad" href="http://stackoverflow.com/questions/251391/why-is-lockthis-bad" target="_blank">מוזמן לקרוא עוד ב stackoverflow</a>.</p>
<h2>משתנים גלובליים &#8211; Static Properties</h2>
<p>מימוש אחר ל caching באמצעות משתנים גלובליים הוא static properties. אנשים נוטים לשכוח את האופציה הזו, והיא לדעתי הרבה יותר נוחה ופשוטה מהשימוש ב Application (או באלטרנטיבת ה NotRemovable של האובייקט Cache). הרעיון פשוט: בסה&quot;כ כותבים class ומוסיפים לו static properties. הם יהיו נגישים בכל האפליקציה. עם זאת, מכיוון שזו סביבת Multi-Threading, נצטרך לנעול איכשהו את המשתנים האלה. מכיוון שזה פתרון ל cache, הרי שאנחנו מצפים לקריאת הנתונים בתדירות גבוהה ולעדכון הנתונים לעיתים רחוקות. לשם כך בדיוק כבר כתבתי את ה Safe Value Pattern <a title="Safe Value Pattern" href="http://heblog.ronklein.co.il/2010/06/safe-value-pattern/" target="_self">מהפוסט הקודם</a>, וניקח את המימוש של SafeValueSeldomWrites. הקוד יראה כך:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">class</span> MyGlobals<br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw1">static</span> SafeValueSeldomWrites<span class="sy0">&lt;</span><span class="kw4">int</span><span class="sy0">&gt;</span> Wish <span class="sy0">=</span> <br />
&nbsp; &nbsp; <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> SafeValueSeldomWrites<span class="sy0">&lt;</span><span class="kw4">int</span><span class="sy0">&gt;</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw1">static</span> SafeValueSeldomWrites<span class="sy0">&lt;</span><span class="kw4">string</span><span class="sy0">&gt;</span> Wall <span class="sy0">=</span> <br />
&nbsp; &nbsp; <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> SafeValueSeldomWrites<span class="sy0">&lt;</span><span class="kw4">string</span><span class="sy0">&gt;</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>את הערכים ההתחלתיים נוכל לשים ב Application_Start, הנה קוד (להמחשה בלבד):</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">void</span> Application_Start<span class="br0">&#40;</span><span class="kw4">object</span> sender, EventArgs e<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; MyGlobals.<span class="me1">Wish</span>.<span class="me1">Value</span> <span class="sy0">=</span> <span class="nu0">1234</span><span class="sy0">;</span><br />
&nbsp; MyGlobals.<span class="me1">Wall</span>.<span class="me1">Value</span> <span class="sy0">=</span> <span class="st0">&quot;numb&quot;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>וניתן כמובן לקרוא אותם או לשנות את הערך שלהם. למשל:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">protected</span> <span class="kw1">void</span> Page_Load<span class="br0">&#40;</span><span class="kw4">object</span> sender, EventArgs e<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="co1">// get a value</span><br />
&nbsp; <span class="kw4">int</span> diamond <span class="sy0">=</span> MyGlobals.<span class="me1">Wish</span>.<span class="me1">Value</span><span class="sy0">;</span></p>
<p>&nbsp; <span class="co1">// set a value</span><br />
&nbsp; MyGlobals.<span class="me1">Wall</span>.<span class="me1">Value</span> <span class="sy0">=</span> <span class="st0">&quot;Vera&quot;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>שימו לב שהפעם אין צורך להמיר ל int את הערך המוחזר.</p>
<h2>יתרונות וחסרונות</h2>
<p>הנה טבלה קטנה שמרכזת את התכונות המרכזיות של השיטות שסקרתי כאן. יתרון יחסי <span style="background-color: #99ff99;">מודגש בירוק</span>.</p>
<table class="ron_heb_table" style="border-color: #3300cc; border-width: 1px;" border="1">
<tbody>
<tr style="background-color: #f5f5f5;" align="right" valign="top">
<td align="right" valign="top"></td>
<td align="right" valign="top"><span style="font-size: small;"><strong>האובייקט Cache</strong></span></td>
<td align="right" valign="top"><span style="font-size: small;"><strong>האובייקט Application</strong></span></td>
<td align="right" valign="top"><span style="font-size: small;"><strong>Static Properties</strong></span></td>
</tr>
<tr valign="top">
<td align="right" valign="top"><strong><span style="font-size: small;">נעילה</span></strong></td>
<td align="right" valign="top"><span style="font-size: small;">גלובלית</span></td>
<td align="right" valign="top"><span style="font-size: small;">גלובלית</span></td>
<td style="background-color: #99ff99;" align="right" valign="top"><span style="font-size: small;">רק על הנתון הרלוונטי</span></td>
</tr>
<tr valign="top">
<td align="right" valign="top"><strong><span style="font-size: small;">casting בשליפת נתונים</span></strong></td>
<td align="right" valign="top"><span style="font-size: small;">צריך</span></td>
<td align="right" valign="top"><span style="font-size: small;">צריך</span></td>
<td style="background-color: #99ff99;" align="right" valign="top"><span style="font-size: small;">לא צריך</span></td>
</tr>
<tr valign="top">
<td align="right" valign="top"><strong><span style="font-size: small;">מתחשב בזכרון השרת</span></strong></td>
<td style="background-color: #99ff99;" align="right" valign="top"><span style="font-size: small;">כן</span></td>
<td align="right" valign="top"><span style="font-size: small;">לא</span></td>
<td align="right" valign="top"><span style="font-size: small;">לא</span></td>
</tr>
<tr valign="top">
<td align="right" valign="top"><strong><span style="font-size: small;">אז מתי להשתמש?</span></strong></td>
<td align="right" valign="top"><span style="font-size: small;">מתי שרק אפשר</span></td>
<td align="right" valign="top"><span style="font-size: small;">כשממירים אפליקציית ASP3 לאפליקציית דוט נט, וגם זה כשלב ביניים בלבד</span></td>
<td align="right" valign="top"><span style="font-size: small;">כשרוצים לשמור ב cache נתונים שנדרשים בתדירות גבוהה, עם צריכת זכרון נמוכה</span></td>
</tr>
</tbody>
</table>
<h1>סיכום</h1>
<p>מימושים שונים ל caching אפשר למצוא בהרבה מקומות במערכות ממוחשבות: יש cache ברמת האפליקציה (כמו שכתבתי בפוסט הזה), יש cache לקריאות מהדיסק ברמת מערכת ההפעלה (ב Windows מספיק מודרני זה חלק מובנה במערכת ההפעלה, ובתקופות קדומות יותר השתמשנו ב <a title="מישהו זוכר מה זה?" href="http://en.wikipedia.org/wiki/SmartDrive" target="_blank">SmartDrive</a> <img src='http://heblog.ronklein.co.il/wp-includes/images/smilies/icon_razz.gif' alt=':-P' class='wp-smiley' /> ). אפרופו הארד-דיסק, גם בתוך הקופסה הזו יש cache (בבקר הפנימי שבתוך הדיסק).</p>
<p>בפוסט הזה, שהתחיל להתגלגל כתוצאה <a target="_blank" href="http://stackoverflow.com/questions/3081972/using-httpcontext-current-application-to-store-simple-data">מתשובה שכתבתי ב stackoverflow</a>,  הצגתי את האפשרויות שיש לנו במסגרת אפליקציית ASP.NET. עם זאת, יש פתרונות Cache יותר מורכבים:</p>
<ul>
<li>יש את <a title="memcached - home" href="http://memcached.org/" target="_blank">memcached</a> שזה שירות אחסון נתונים בזכרון (כאפליקציה נפרדת, עם API משלה).</li>
<li>יש אפשרות לאחסן נתונים ב cache מבוזר (distributed cache). מיועד לאפליקציות שמותקנות על מספר שרתים.</li>
</ul>
<p>בקיצור, גם cache אפליקטיבי יכול להיות מורכב, הכל תלוי במורכבות של האפליקציה עצמה ובעומס עליה.</p>
<p>אגב, בנוסף ל Cache הרגיל מתוך ה Web, בדוט נט 4 כבר הכניסו אובייקט cache שזמין גם ל Console Applications. קוראים לזה MemoryCache, ופרטים נוספים <a title="MemoryCache - MSDN" href="http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.aspx" target="_blank">תמצאו ב MSDN</a>. ה MemoryCache הזה ממחיש את הצורך ב Caching שלא רק בקונטקסט של אפליקציות ווביות, ונראה שמיקרוסופט נענו לצורך הזה. יופי מיקרוסופט, עכשיו רק נשאר להיפטר מה IDisposable המזוויע הזה <img src='http://heblog.ronklein.co.il/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> </p>
<p>כרגיל, תכנות נעים ויעיל!</p>
</div>]]></content:encoded>			<wfw:commentRss>http://heblog.ronklein.co.il/2010/06/web-caching-techniques/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8235;Safe Value Pattern&#8236;</title>		<link>http://heblog.ronklein.co.il/2010/06/safe-value-pattern/</link>
		<comments>http://heblog.ronklein.co.il/2010/06/safe-value-pattern/#comments</comments>
		<pubDate>Tue, 22 Jun 2010 23:00:02 +0000</pubDate>
		<dc:creator>&#8235;רון קליין&#8236;</dc:creator>				<category><![CDATA[תכנות]]></category>
		<category><![CDATA[locking]]></category>
		<category><![CDATA[Mini-Pattern]]></category>
		<category><![CDATA[Multi-Threading]]></category>

		<guid isPermaLink="false">http://heblog.ronklein.co.il/?p=529</guid>
		<description><![CDATA[&#8235;בפוסט הזה: קוד קצר ולעניין של משתנה שקוראים אותו ומעדכנים אותו בסביבה שהיא Multi-Threaded. כידוע, כדי להגן על המשתנה מפני corruption אנחנו צריכים נעילה (כן, אני יודע, יש גם את Interlock, אבל זה לא העניין פה, אל תטרידו אותי עם עובדות&#8230;) אז במקום להצהיר בכל פעם על משתנה ועל האובייקט שנועל אותו, הנה Mini-Pattern שעושה [...]&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p>בפוסט הזה: קוד קצר ולעניין של משתנה שקוראים אותו ומעדכנים אותו בסביבה שהיא Multi-Threaded.</p>
<p>כידוע, כדי להגן על המשתנה מפני corruption אנחנו צריכים נעילה (כן, אני יודע, יש גם את Interlock, אבל זה לא העניין פה, אל תטרידו אותי עם עובדות&#8230;)</p>
<p>אז במקום להצהיר בכל פעם על משתנה ועל האובייקט שנועל אותו, הנה Mini-Pattern שעושה את העבודה:</p>
<h3>הגירסה הפשוטה</h3>
<p>מנעול פשוט, עבודה עם lock:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">namespace</span> Pepperoni.<span class="me1">Lib</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> SafeValue<span class="sy0">&lt;</span>T<span class="sy0">&gt;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> <span class="kw4">object</span> locker <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> <span class="kw4">object</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> T innerValue<span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> SafeValue<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> SafeValue<span class="br0">&#40;</span>T initialValue<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; innerValue <span class="sy0">=</span> initialValue<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> T Value<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; get<br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">lock</span> <span class="br0">&#40;</span>locker<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> innerValue<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; set<br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">lock</span> <span class="br0">&#40;</span>locker<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; innerValue <span class="sy0">=</span> value<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>אין הרבה מה לכתוב ב code-review, בסה&quot;כ גם ב getter וגם ב setter נועלים אובייקט פנימי ורק אז מחזירים את הערך (ב getter) או מעדכנים את הערך (ב setter). קצר, פשוט, קריא, עובד (נראה לי שאני מאמץ את זה בראשי תיבות: קפק&quot;ע! <img src='http://heblog.ronklein.co.il/wp-includes/images/smilies/icon_razz.gif' alt=':-P' class='wp-smiley' /> ).</p>
<h3>אלטרנטיבה &#8211; הרבה קריאות, מעט עדכונים</h3>
<p>אם אנחנו יודעים שיש הרבה יותר קריאות של הערך לעומת עדכונים של הערך, כלומר הרבה יותר שימושים ב getter מאשר ב setter, אז נוכל לייעל את העבודה אם נשתמש באובייקט ReaderWriterLockSlim (לקריאה נוספת &#8211; <a title="ReaderWriterLockSlim - MSDN" href="http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx" target="_blank">MSDN</a>). הנה הקוד:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System.Threading</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Pepperoni.<span class="me1">Lib</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> SafeValueSeldomWrites<span class="sy0">&lt;</span>T<span class="sy0">&gt;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> T innerValue<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> ReaderWriterLockSlim locker <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ReaderWriterLockSlim<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">public</span> SafeValueSeldomWrites<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> SafeValueSeldomWrites<span class="br0">&#40;</span>T initialValue<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; innerValue <span class="sy0">=</span> initialValue<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> T Value<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; get<br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; locker.<span class="me1">EnterReadLock</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; T result <span class="sy0">=</span> innerValue<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; locker.<span class="me1">ExitReadLock</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> result<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; set<br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; locker.<span class="me1">EnterWriteLock</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; innerValue <span class="sy0">=</span> value<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; locker.<span class="me1">ExitWriteLock</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>גם כאן הקוד יחסית פשוט וקריא, הנעילה מתבצעת עם ה ReaderWriterLockSlim שמוצהר כ private.</p>
<h3>עוד כמה מילים</h3>
<p>אם רוצים, אז אפשר לעשות כאן אבסטרקציה לשני המימושים (ע&quot;י interface או base-class). אבל זה נראה לי מיותר, כי בד&quot;כ יודעים כבר בזמן הפיתוח עצמו באיזה מימוש להשתמש.</p>
<p>עניין נוסף: במקרים מסויימים אפשר לעבוד עם Interlock, שמאפשר קריאה/כתיבה בסביבה שהיא Multi-Threaded, ללא שימוש ב lock הסטנדרטי. מומלץ <a title="Google Search - Interlock" href="http://www.google.com/search?q=interlock+C%23+%22.net%22" target="_blank">לגגל</a> או לקרוא ב <a title="MSDN - Interlock" href="http://msdn.microsoft.com/en-us/library/system.threading.interlocked.aspx" target="_blank">MSDN</a>.</p>
<p>אפשר <a title="Save Value - Source Code - Download" href="http://heblog.ronklein.co.il/wp-content/uploads/2010/06/Pepperoni.zip">להוריד מכאן</a> את הקוד, למי ש copy-paste גדול עליו <img src='http://heblog.ronklein.co.il/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /><br />
תכנות נעים!</p>
</div>]]></content:encoded>			<wfw:commentRss>http://heblog.ronklein.co.il/2010/06/safe-value-pattern/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8235;Modulus Counter &#8211; Code Reuse vs. Micro Class&#8236;</title>		<link>http://heblog.ronklein.co.il/2010/06/modulus-counter-code-reuse-vs-micro-class/</link>
		<comments>http://heblog.ronklein.co.il/2010/06/modulus-counter-code-reuse-vs-micro-class/#comments</comments>
		<pubDate>Wed, 02 Jun 2010 23:00:52 +0000</pubDate>
		<dc:creator>&#8235;רון קליין&#8236;</dc:creator>				<category><![CDATA[תכנות]]></category>
		<category><![CDATA[Code-Reuse]]></category>
		<category><![CDATA[mod]]></category>
		<category><![CDATA[Over-Engineering]]></category>
		<category><![CDATA[State Machine]]></category>

		<guid isPermaLink="false">http://heblog.ronklein.co.il/?p=476</guid>
		<description><![CDATA[&#8235;בפוסט הזה אני אראה קוד שנראה די פשוט, שלא לומר על סף הטריוויאלי, ואח"כ נראה אם הוא מתאים למשימות פרקטיות. לאחר מכן נבדוק האם המחלקה ModCounter שמוצגת כאן היא over-engineering או באמת "חומר טוב" לשימוש חוזר בקוד.&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p>הערה מקדימה: לפעמים אפשר לכתוב קוד סתם בשביל הכיף.</p>
<p>בפוסט הזה אני אראה קוד שנראה די פשוט, שלא לומר על סף הטריוויאלי, ואח&quot;כ נראה אם הוא מתאים למשימות פרקטיות. לאחר מכן נבדוק האם המחלקה ModCounter שמוצגת כאן היא over-engineering או באמת &quot;חומר טוב&quot; לשימוש חוזר בקוד.</p>
<p>ברמת הקוד, הנושא העיקרי כאן הוא פעולת המודולו, שמסומנת ב <span class="ron_english">C#</span> ע&quot;י הסימן אחוז (%).</p>
<p>אז מה יהיה כאן:</p>
<ul>
<li>דוגמה פשוטה להתחלה</li>
<li>מעבר מ inline-code ל class</li>
<li>סתם בשביל הכיף &#8211; שעון</li>
<li>בשביל הפרקטיקה &#8211; מוניטור לכשלונות רצופים</li>
<li>סיכום &#8211; האם הגזמנו עם ה ModCounter הזה?</li>
</ul>
<h1>דוגמה פשוטה להתחלה</h1>
<p>קצת קוד מעולם לא הזיק לאיש. אז בשביל הסיפתח:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Threading</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">ConsoleApp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw4">class</span> Program<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">&#40;</span><span class="kw4">string</span><span class="br0">&#91;</span><span class="br0">&#93;</span> args<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;started&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; DoSomeLengthyActivity<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;done, press ENTER to quit&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">ReadLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">static</span> <span class="kw1">void</span> DoSomeLengthyActivity<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> i <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> i <span class="sy0">&lt;</span> <span class="nu0">100</span><span class="sy0">;</span> i<span class="sy0">++</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Thread.<span class="me1">Sleep</span><span class="br0">&#40;</span>100<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>המתודה DoSomeLengthyActivity מייצגת פעילות ממושכת בלולאה. כלומר פעילות שכל איטרציה שלה לוקחת די הרבה זמן (נניח 100 מילישניות) ויש לה די הרבה איטרציות כאלו.<br />
החלק המהותי הוא ש DoSomeLengthyActivity היא מתודה ארוכה לביצוע ויש בה לולאה (או כל פירוק אחר להרבה מאוד תתי-משימות).</p>
<p>התוצאה היא שהמשתמש רואה הודעת פתיחה, ואז לא רואה כלום במשך 10 שניות, ועלול לחשוב שהתוכנה נתקעה. רק אחרי 10 שניות הוא מקבל את הודעת הסיום.<br />
כדי להימנע מזה, נוכל להוסיף הודעה בכל איטרציה, כך שלמשתמש תהיה אינדיקציה שהכל עובד:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Threading</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">ConsoleApp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw4">class</span> Program<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">&#40;</span><span class="kw4">string</span><span class="br0">&#91;</span><span class="br0">&#93;</span> args<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;started&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; DoSomeLengthyActivity<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;done, press ENTER to quit&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">ReadLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">static</span> <span class="kw1">void</span> DoSomeLengthyActivity<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> i <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> i <span class="sy0">&lt;</span> <span class="nu0">100</span><span class="sy0">;</span> i<span class="sy0">++</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;still working&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Thread.<span class="me1">Sleep</span><span class="br0">&#40;</span>100<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>אלא שעכשיו המתשמש יראה 100 הודעות שרצות מהר מאוד. אמנם יש למשתמש אינדיקציה שהתוכנה פעילה, אבל עכשיו הוא מוצף באינדיקציות כאלו, ויכול להתעלם מכולן או מחלקן כאילו היו רעש לבן.</p>
<p>אז כמו כל דבר בחיים, הסוד הוא המינון, ולא צריך להפריז. נוכל לאזן את האינדיקציות ע&quot;י בדיקה קטנה של הערך של i, באופן הבא:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Threading</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">ConsoleApp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw4">class</span> Program<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">&#40;</span><span class="kw4">string</span><span class="br0">&#91;</span><span class="br0">&#93;</span> args<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;started&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; DoSomeLengthyActivity<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;done, press ENTER to quit&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">ReadLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">static</span> <span class="kw1">void</span> DoSomeLengthyActivity<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> i <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> i <span class="sy0">&lt;</span> <span class="nu0">100</span><span class="sy0">;</span> i<span class="sy0">++</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>i <span class="sy0">%</span> <span class="nu0">10</span> <span class="sy0">==</span> <span class="nu0">0</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;still working&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Thread.<span class="me1">Sleep</span><span class="br0">&#40;</span>100<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>הגענו אל האיזון הנכסף: המשתמש מקבל אינדיקציה כל 10 איטרציות, שזה בערך פעם בשניה.<br />
אבל מי שמתעסק עם הקוד הזה לא בהכרח מבין מיידית למה עובדים עם המשתנה i ועושים עליו modulus.<br />
ומעבר לזה, לא ניתן לעבוד עכשיו עם לולאה מסוג אחר, כמו foreach, אלא רק עם לולאת for, כי הלוגיקה של האינדיקציות למשתמש &quot;כבולה&quot; למשתנה הלולאה.</p>
<h1>הרשו לי להציג: ModCounter</h1>
<p>הלוגיקה של פעולת המודולו יכולה, אם כך, לעבור למחלקה נפרדת, נקרא לה ModCounter. בתור התחלה, הקוד נראה כך:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">class</span> ModCounter<br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> <span class="kw4">int</span> modBase<span class="sy0">;</span><br />
&nbsp; <span class="kw1">private</span> <span class="kw4">int</span> theValue<span class="sy0">;</span></p>
<p>&nbsp; <span class="kw1">public</span> ModCounter<span class="br0">&#40;</span><span class="kw4">int</span> modBase<span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw1">this</span> <span class="br0">&#40;</span>modBase, 0<span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> ModCounter<span class="br0">&#40;</span><span class="kw4">int</span> modBase, <span class="kw4">int</span> initialValue<span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>modBase <span class="sy0">&lt;=</span> 1<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentOutOfRangeException<span class="br0">&#40;</span><span class="st0">&quot;modBase&quot;</span>, <span class="st0">&quot;value must be greater than 1&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">modBase</span> <span class="sy0">=</span> modBase<span class="sy0">;</span><br />
&nbsp; &nbsp; theValue <span class="sy0">=</span> initialValue <span class="sy0">%</span> modBase<span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> <span class="kw4">int</span> Increase<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; var increased <span class="sy0">=</span> theValue <span class="sy0">+</span> <span class="nu0">1</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>increased <span class="sy0">==</span> modBase<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; increased <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span><br />
&nbsp; &nbsp; theValue <span class="sy0">=</span> increased<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> increased<span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> <span class="kw4">bool</span> IsZero<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; get<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">return</span> theValue <span class="sy0">==</span> <span class="nu0">0</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> <span class="kw4">int</span> Value<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; get<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">return</span> theValue<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>לפני סקירת הקוד, בואו נראה את השימוש הפשוט ב class הזה</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">&#40;</span><span class="kw4">string</span><span class="br0">&#91;</span><span class="br0">&#93;</span> args<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;ModCounter example&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; ModCounter myCounter <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ModCounter<span class="br0">&#40;</span>3<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>myCounter.<span class="me1">Value</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// value is 0</span><br />
&nbsp; myCounter.<span class="me1">Increase</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>myCounter.<span class="me1">Value</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// value is 1</span><br />
&nbsp; myCounter.<span class="me1">Increase</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>myCounter.<span class="me1">Value</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// value is 2</span><br />
&nbsp; myCounter.<span class="me1">Increase</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>myCounter.<span class="me1">Value</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// value is 0</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>myCounter.<span class="me1">IsZero</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// True</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;done, press ENTER to quit&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; Console.<span class="me1">ReadLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>והפלט:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">ModCounter example<br />
0<br />
1<br />
2<br />
0<br />
True<br />
done, press ENTER to quit</div>
</div>
<h3>סקירה קצרה על הקוד</h3>
<ul>
<li>הקונסטרקטור &#8211; מקבל בסיס למודולו (חייב להיות גדול מ 1) וערך התחלתי כלשהו (ברירת מחדל &#8211; אפס). הערך ההתחלתי מנורמל לתחום שבין 0 לבסיס.</li>
<li>השימוש בפעולת המודולו עצמה מתבצע רק פעם אחת &#8211; בקונסטרקטור. הרווחנו משהו, כי זו נחשבת פעולה &quot;יקרה&quot;, אבל זה רווח זניח.</li>
<li>פעולת Increase מוסיפה 1 לערך הנוכחי, ואם הוא מגיע לגבול העליון &#8211; הערך מתאפס.</li>
</ul>
<p>עכשיו נכניס את ה ModCounter לתוכנית המקורית ונראה איך הקוד משתנה</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">private</span> <span class="kw1">static</span> <span class="kw1">void</span> DoSomeLengthyActivity<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; var myCounter <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ModCounter<span class="br0">&#40;</span>10<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> i <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> i <span class="sy0">&lt;</span> <span class="nu0">100</span><span class="sy0">;</span> i<span class="sy0">++</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; myCounter.<span class="me1">Increase</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>myCounter.<span class="me1">IsZero</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;still working&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; Thread.<span class="me1">Sleep</span><span class="br0">&#40;</span>100<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>מה שקבלנו בינתיים זה שהאינדיקציות אינן תלויות במשתנה הלולאה (i), אבל עדיין התחושה היא שהקוד לא מספיק קריא. אולי נשפר קצת את ה ModCounter כך שיהיה לו event באופן הבא:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">class</span> ModCounter<br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw1">event</span> Action OnZero<span class="sy0">;</span></p>
<p>&nbsp; <span class="kw1">private</span> <span class="kw1">void</span> InvokeOnZero<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; Action action <span class="sy0">=</span> OnZero<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>action <span class="sy0">!=</span> <span class="kw1">null</span><span class="br0">&#41;</span> action<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="co1">// .. code ..</span></p>
<p>&nbsp; <span class="kw1">public</span> <span class="kw4">int</span> Increase<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; var increased <span class="sy0">=</span> theValue <span class="sy0">+</span> <span class="nu0">1</span><span class="sy0">;</span><br />
&nbsp; &nbsp; var shouldResetToZero <span class="sy0">=</span> increased <span class="sy0">==</span> modBase<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>shouldResetToZero<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; theValue <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; InvokeOnZero<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; theValue <span class="sy0">=</span> increased<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> theValue<span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>ועכשיו נוכל לכתוב את הקוד המקורי בצורה קצת יותר קריאה:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">private</span> <span class="kw1">static</span> <span class="kw1">void</span> DoSomeLengthyActivity<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; var myCounter <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ModCounter<span class="br0">&#40;</span>10<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; myCounter.<span class="me1">OnZero</span> <span class="sy0">+=</span> <span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">=&gt;</span> Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;still working&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> i <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> i <span class="sy0">&lt;</span> <span class="nu0">100</span><span class="sy0">;</span> i<span class="sy0">++</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; myCounter.<span class="me1">Increase</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; Thread.<span class="me1">Sleep</span><span class="br0">&#40;</span>100<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>לסיכום שלב א' &#8211; העברנו את הלוגיקה של האינדיקציות ממשתנה הלולאה אל מחלקה חיצונית בשם ModCounter.<br />
בתוך ה ModCounter, כל קריאה למתודה Increase מקדמת את הערך הפנימי ב 1, ומתבצעת בדיקה: אם הוא הגיע לגבול העליון אז הוא מתאפס והאירוע OnZero מופעל.</p>
<h1>בשביל הכיף &#8211; ClockCounter</h1>
<p>נשחק קצת עם ה ModCounter שלנו וניצור מחלקה חדשה בשם ClockCounter, שמדמה שעון, שאפשר לתת לו ערך התחלתי ולקדם אותו בשניה מתי שרוצים.</p>
<p>נכון, המלעיזים יגידו שאפשר לעשות את זה עם TimeSpan, אבל זה רק בשביל הכיף, אז המלעיזים יכולים להלעיז חופשי, אני בכלל עם אוזניות.</p>
<p>הקוד? הנה הקוד:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">class</span> ClockCounter<br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> ModCounter seconds<span class="sy0">;</span><br />
&nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> ModCounter minutes<span class="sy0">;</span><br />
&nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> ModCounter hours<span class="sy0">;</span></p>
<p>&nbsp; <span class="kw1">public</span> ClockCounter<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw1">this</span><span class="br0">&#40;</span>0, 0, 0<span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> ClockCounter<span class="br0">&#40;</span>TimeSpan initialTime<span class="br0">&#41;</span> <span class="sy0">:</span> <br />
&nbsp; &nbsp; <span class="kw1">this</span><span class="br0">&#40;</span>initialTime.<span class="me1">Hours</span>, initialTime.<span class="me1">Minutes</span>, initialTime.<span class="me1">Seconds</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> ClockCounter<span class="br0">&#40;</span><span class="kw4">int</span> hours, <span class="kw4">int</span> minutes, <span class="kw4">int</span> seconds<span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">seconds</span> <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ModCounter<span class="br0">&#40;</span>60, seconds<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">minutes</span> <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ModCounter<span class="br0">&#40;</span>60, minutes<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">hours</span> <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ModCounter<span class="br0">&#40;</span>24, hours<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">seconds</span>.<span class="me1">OnZero</span> <span class="sy0">+=</span> <span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">=&gt;</span> <span class="kw1">this</span>.<span class="me1">minutes</span>.<span class="me1">Increase</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">minutes</span>.<span class="me1">OnZero</span> <span class="sy0">+=</span> <span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">=&gt;</span> <span class="kw1">this</span>.<span class="me1">hours</span>.<span class="me1">Increase</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> <span class="kw1">void</span> IncreaseSecond<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">seconds</span>.<span class="me1">Increase</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> TimeSpan Time<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; get<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> TimeSpan<span class="br0">&#40;</span>hours.<span class="me1">Value</span>, minutes.<span class="me1">Value</span>, seconds.<span class="me1">Value</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>והשימוש בקוד:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">&#40;</span><span class="kw4">string</span><span class="br0">&#91;</span><span class="br0">&#93;</span> args<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;started ClockCounter example&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; var clockCounter <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ClockCounter<span class="br0">&#40;</span>22, 59, 59<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>clockCounter.<span class="me1">Time</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; clockCounter.<span class="me1">IncreaseSecond</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>clockCounter.<span class="me1">Time</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;now let's try again&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; clockCounter <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ClockCounter<span class="br0">&#40;</span>23, 59, 59<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>clockCounter.<span class="me1">Time</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; clockCounter.<span class="me1">IncreaseSecond</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>clockCounter.<span class="me1">Time</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;done, press ENTER to quit&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; Console.<span class="me1">ReadLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>והפלט, כצפוי:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">started ClockCounter example<br />
22:59:59<br />
23:00:00<br />
now let's try again<br />
23:59:59<br />
00:00:00<br />
done, press ENTER to quit</div>
</div>
<p>חביב? -בהחלט. כמעט כל הקוד הוא ב wiring-up בקונסטרקטור, וזה מגניב. שימושי? -לא ממש. <img src='http://heblog.ronklein.co.il/wp-includes/images/smilies/icon_razz.gif' alt=':-P' class='wp-smiley' /> </p>
<h1>בשביל הפרקטיקה &#8211; HealthMonitor</h1>
<div id="attachment_496" class="wp-caption alignleft" style="width: 190px"><a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/06/Stethoscope_w180_h240.jpg"><img src="http://heblog.ronklein.co.il/wp-content/uploads/2010/06/Stethoscope_w180_h240.jpg" alt="" title="Stethoscope_w180_h240" width="180" height="240" class="size-full wp-image-496" /></a><p class="wp-caption-text">עוד Health Monitor</p></div>
<p>בואו ננסה בכל זאת לעשות משהו פרקטי &#8211; מוניטור על פעילות תקינה של מערכת.</p>
<p>נניח שיש לנו Web-Service שאמור לקבל request-ים לעיתים מזדמנות, כלומר לא באופן אינטנסיבי ולא לעיתים רחוקות, נניח פעם בחצי שעה (בממוצע).</p>
<p>ה Web-Service הזה צריך לבצע הרבה פעולות לכל request, ובסופן יש לו מדד אחד ויחיד: הצליח או נכשל. &quot;הצליח&quot; משמעותו שהמערכת קלטה בהצלחה את ה request וטיפלה בו במלואו, ו&quot;נכשל&quot; משמעותו שמשהו השתבש בדרך, ואין מה לעשות כרגע. ההסתברות לשיבוש היא לא זניחה, ונרצה לעשות קצת live-monitoring על המערכת שלנו. הלוגיקה של ה monitoring (בעברית זה &quot;ניטור&quot;) היא פשוטה: אחרי X כשלונות רצופים &#8211; להודיע משהו למישהו במייל. במידה שהיתה הצלחה אחרי כשלון מספר 1, 2, או כל ערך קטן מ X &#8211; מונה הכשלונות מתאפס ומתחילים מחדש.</p>
<p>לשם כך נצטרך רק להוסיף את המתודה Reset ב ModCounter שלנו (שפשוט תאפס את המונה הפנימי שלו) ולעטוף אותו ב class חדש שנקרא לו HealthMonitor, שנראה כך:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">class</span> HealthMonitor<br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">int</span> MaxConsequtiveFailures <span class="br0">&#123;</span> get<span class="sy0">;</span> <span class="kw1">private</span> set<span class="sy0">;</span> <span class="br0">&#125;</span><br />
&nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> ModCounter modCounter<span class="sy0">;</span></p>
<p>&nbsp; <span class="kw1">public</span> HealthMonitor<span class="br0">&#40;</span><span class="kw4">int</span> maxConsequtiveFailures<span class="br0">&#41;</span> <span class="sy0">:</span> <br />
&nbsp; &nbsp; <span class="kw1">this</span><span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ModCounter<span class="br0">&#40;</span>maxConsequtiveFailures<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; MaxConsequtiveFailures <span class="sy0">=</span> maxConsequtiveFailures<span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">private</span> HealthMonitor<span class="br0">&#40;</span>ModCounter modCounter<span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">modCounter</span> <span class="sy0">=</span> modCounter<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">modCounter</span>.<span class="me1">OnZero</span> <span class="sy0">+=</span> InvokeReachedMaxConsequentFailures<span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Pass<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; modCounter.<span class="me1">Reset</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Fail<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; modCounter.<span class="me1">Increase</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> <span class="kw1">event</span> Action ReachedMaxConsequentFailures<span class="sy0">;</span></p>
<p>&nbsp; <span class="kw1">private</span> <span class="kw1">void</span> InvokeReachedMaxConsequentFailures<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; Action failures <span class="sy0">=</span> ReachedMaxConsequentFailures<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>failures <span class="sy0">!=</span> <span class="kw1">null</span><span class="br0">&#41;</span> failures<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>השימוש ב HealthMonitor מאפשר להפריד בין הלוגיקה של דיווח על הצלחה/כשלון (שמגיע מתוך ה Web-Service) לבין הטיפול ברצף של כשלונות (שיכול להיות בכל מקום אחר באפליקציה), מתוך הנחה שיש instance יחיד של ה HealthMonitor באפליקציה שלנו. אפשר לממש את זה בכל מיני צורות (סינגלטון או סתם משתנה סטאטי באחת המחלקות של האפליקציה). לצורך הדוגמה, ניקח את המקרה הפשוט של משתנה סטאטי זמין לכל, ונאתחל אותו ב Global.asax. הנה הקוד:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">class</span> Global <span class="sy0">:</span> <span class="kw5">System.<span class="me1">Web</span></span>.<span class="me1">HttpApplication</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw1">static</span> HealthMonitor HealthMonitor<span class="sy0">;</span></p>
<p>&nbsp; <span class="kw1">protected</span> <span class="kw1">void</span> Application_Start<span class="br0">&#40;</span><span class="kw4">object</span> sender, EventArgs e<span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; HealthMonitor <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> HealthMonitor<span class="br0">&#40;</span>3<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; HealthMonitor.<span class="me1">ReachedMaxConsequentFailures</span> <span class="sy0">+=</span> SendMailToAppAdmin<span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">void</span> SendMailToAppAdmin<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co1">// code to send a warning mail </span><br />
&nbsp; &nbsp; <span class="co1">// to the application administrator</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="co1">// .. code ..</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>ואז הקוד שלנו ב Web-Service יראה כך:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">class</span> MyService <span class="sy0">:</span> <span class="kw5">System.<span class="me1">Web</span>.<span class="me1">Services</span></span>.<span class="me1">WebService</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="br0">&#91;</span>WebMethod<span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw1">void</span> DoSomething<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">bool</span> allOk <span class="sy0">=</span> false<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="co1">// do a lot of stuff,</span><br />
&nbsp; &nbsp; <span class="co1">// set allOk to false if needed</span><br />
&nbsp; &nbsp; <span class="co1">// &#8230;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>allOk<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; Global.<span class="me1">HealthMonitor</span>.<span class="me1">Pass</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; Global.<span class="me1">HealthMonitor</span>.<span class="me1">Fail</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<h3>סקירה קצרה</h3>
<ul>
<li>הקוד של ה Web-Service לא <strong>מטפל</strong> ברצף של כשלונות, הוא רק <strong>מודיע</strong> על הצלחה או כשלון.</li>
<li>הקוד הוא לדוגמה בלבד, ואין כאן התייחסות להיבט של Multi-Threading (שאכן מתקיים בסביבת Web).</li>
<li>הקוד המוצג כאן מגדיל את הצימוד בין ה Web-Service לבין המחלקה Global (שהיא ה Global.asax.cs) שלנו. וזה, במילים עדינות, לא להיט.</li>
</ul>
<p>אני רק מציין את הדברים האלה, כדי שזה יהיה איפשהו בראש אם מישהו יאמץ את הדוגמאות כאן, אבל אני לא משנה את הקוד המוצע, פשוט כי זה לא הפוקוס של הפוסט.</p>
<h1>סיכום</h1>
<p>בפוסט הזה הצגתי מחלקה סופר-פשוטה בשם ModCounter. כל תפקידה הוא קידום מונה פנימי שישמור תוצאה שהיא מודולו בסיס מסויים. תוך כדי הפוסט הרחבתי אותה קצת פה ושם, והיא עדיין נשארה פשוטה. למעשה ה ModCounter הוא state-machine (ואני, מה לעשות, די מחבב state-machines). הראיתי שימוש פרקטי ב ModCounter ע&quot;י מחלקה שעוטפת אותו, שמבצעת monitoring למערכת. האם הייתי משתמש ב ModCounter בפועל? התשובה היא כן, המחלקה HealthMonitor היא לא המצאה בשביל הפוסט, היא באמת נכתבה למערכת קיימת (עם שינויים קלים).</p>
<div id="attachment_499" class="wp-caption alignleft" style="width: 250px"><a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/06/yin_yang.jpg"><img src="http://heblog.ronklein.co.il/wp-content/uploads/2010/06/yin_yang.jpg" alt="" title="ין ינג" width="240" height="180" class="size-full wp-image-499" /></a><p class="wp-caption-text">גם זה איזון</p></div>
<p>ועדיין נשארת אצלי השאלה, האם באמת כדאי להכניס לוגיקה יחסית פשוטה לתוך class. איפה עובר הגבול בין good-practice לבין over-engineering (&quot;הינדוס יתר&quot;). היתרון הגדול של לוגיקה פשוטה בתוך מחלקה משל עצמה הוא שאפשר לבדוק אותה בקלות. מרגע שהיא נבדקה ונמצאה תקינה (עד כמה שאפשר) &#8211; אפשר להשתמש במחלקה הזו בכל הזדמנות, ואז גם קבלנו שימוש חוזר בקוד. יתרה מכך, זה קוד שאנחנו סומכים עליו שהוא תקין.</p>
<p>החסרון הוא מהצד של over-engineering &#8211; פעולות כל כך פשוטות יכולות להישאר כקוד שחוזר על עצמו. אין טעם ליצור micro-class, כי אז הקוד הופך להיות לא מספיק קריא. וזה עוד לפני שדברנו על להוסיף רפרנס לאסמבלי/פרוייקט חיצוני.</p>
<p>אז חזרנו לדרך האמצע, לאיזון הנכסף, בין קוד קריא לקוד שקל לתחזק אותו. בין שימוש חוזר לבין over-engineering. במה אתם תבחרו? איפה אתם עומדים בסקאלה הזו?</p>
<p>האמת, בלי קשר לבחירה שלכם &#8211; כמו שכתבתי בתחילת הפוסט, לפעמים זה כיף לכתוב קוד גם אם הוא לא הכי שימושי <img src='http://heblog.ronklein.co.il/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> .</p>
<p>תכנות נעים!</p>
<p>וכמובן &#8211; <a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/06/mod-counter.zip">כל הקוד זמין כאן</a>.</p>
</div>]]></content:encoded>			<wfw:commentRss>http://heblog.ronklein.co.il/2010/06/modulus-counter-code-reuse-vs-micro-class/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8235;UDP Messaging &#8211; The SOLID Way&#8236;</title>		<link>http://heblog.ronklein.co.il/2010/05/udp-messaging-the-solid-way/</link>
		<comments>http://heblog.ronklein.co.il/2010/05/udp-messaging-the-solid-way/#comments</comments>
		<pubDate>Sat, 22 May 2010 20:00:34 +0000</pubDate>
		<dc:creator>&#8235;רון קליין&#8236;</dc:creator>				<category><![CDATA[תכנות]]></category>
		<category><![CDATA[Dependency-Injection]]></category>
		<category><![CDATA[DRY]]></category>
		<category><![CDATA[Echo-Server]]></category>
		<category><![CDATA[Listener]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[Refactoring]]></category>
		<category><![CDATA[Solid]]></category>
		<category><![CDATA[SRP]]></category>
		<category><![CDATA[UDP]]></category>
		<category><![CDATA[Unit-Testing]]></category>

		<guid isPermaLink="false">http://heblog.ronklein.co.il/?p=429</guid>
		<description><![CDATA[&#8235;הפוסט הזה הוא לא על UDP. כלומר, כן, הוא בהחלט על UDP. אבל הוא גם, ובעיקר, על כתיבת קוד נכונה, על OOP, על Dependency Injection, על Refactoring, ועל Design. וגם, מה המחיר של כתיבת קוד &#34;נכונה&#34;, ואולי זה לא תמיד משתלם? בפוסט הזה אני אציג UDP Listener די פשוט, שמבוסס על המחלקה UdpClient שבאה עם [...]&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p>הפוסט הזה הוא לא על UDP. כלומר, כן, הוא בהחלט על UDP. אבל הוא גם, ובעיקר, על כתיבת קוד נכונה, על OOP, על Dependency Injection, על Refactoring, ועל Design. וגם, מה המחיר של כתיבת קוד &quot;נכונה&quot;, ואולי זה לא תמיד משתלם?</p>
<p>בפוסט הזה אני אציג UDP Listener די פשוט, שמבוסס על המחלקה UdpClient שבאה עם הדוט נט.</p>
<p>בהמשך אני אציג מחלקות נוספות שמתבססות על ה UDP Listener הזה. למשל: UDP Echo Server, וכן רכיב Forward, או &quot;גב אל גב&quot;.</p>
<p>וכאמור, תוך כדי הפוסט אנסה להמחיש חלק מעקרונות תכנות שנקראים <a target="_blank" title="Solid (object-oriented design) - Wikipedia" href="http://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29">SOLID</a>, עקרונות שמנחים אותנו לכתוב ולתכנן רכיבי תוכנה בצורה טובה. טובה מאוד. ואולי טובה מדי? <img src='http://heblog.ronklein.co.il/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p>נתחיל מההתחלה: מה זה UDP בכלל, ולמה זה עוזר לנו?</p>
<h1>כמה מילים על UDP</h1>
<p>תקשורת בין מחשבים נעשית כיום בשתי דרכים עיקריות: TCP ו UDP.</p>
<p>תקשורת ב UDP היא כזו ששולחת חבילות מידע בצורה שהיא lossy: חבילות  יכולות להגיע ליעדן ויכולות גם ליפול איפשהו. זה שימושי למשל בהזרמת אודיו/וידאו, כי שם אם איזשהו פריים &quot;נפל&quot;, אז אפשר לעבור לפריים הבא, ושלום על ישראל. גם ב log4net ניתן לראות שימוש ב UDP, כפי שכתבתי בפוסטים קודמים. ראשי התיבות של UDP הן User Datagram Protocol. אפשר לומר, בתרגום חופשי, ש Datagram זו חבילת מידע שעוברת בפרוטוקול הזה.</p>
<p>ניכנס קצת יותר לפרטים. כל תקשורת ב UDP יוצאת ממחשב A דרך פורט X ומגיעה אל מחשב B, אל פורט Y.  לפעמים A ו B הם אותו המחשב. בד&quot;כ פורט היציאה (X) הוא רנדומלי, אבל הוא  קיים. התמונה להמחשה בלבד:</p>
<div id="attachment_432" class="wp-caption alignnone" style="width: 552px"><a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/05/udp_port.png"><img class="size-full wp-image-432" title="udp_port" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/05/udp_port.png" alt="UDP Message sent from A:X to B:Y" width="542" height="110" /></a><p class="wp-caption-text">הודעת UDP שנשלחת ממחשב A ומפורט X למחשב B לפורט Y.</p></div>
<p>הצד שמקבל את חבילת המידע ב UDP יודע גם מאיפה היא יצאה. כלומר, יחד עם  חבילת המידע יש כמה מאפיינים נוספים כמו הכתובת של השולח (A) ופורט היציאה (X)</p>
<p>בפוסט הזה אני אראה קוד של הרכיב שמקבל הודעות UDP. אני אבנה את הרכיב הזה בצורה יחסית פשוטה, ואח&quot;כ נוכל בקלות להרחיב את הקוד כדי לקבל עוד פיצ'רים.</p>
<h1>My UDP Listener</h1>
<p>ניגש לקוד עצמו. מה שיש לנו, כנקודת מוצא, זה class שיורש מ Startable Base שהוא Thread Safe. את ה Startable הזה הכרנו <a title="Startable State Machine" href="http://heblog.ronklein.co.il/2010/04/startable-state-machine-part-2/">בפוסט קודם</a>, ואני אזכיר שזה base-class שאחראי על ניהול start/stop.</p>
<p>בנוסף יש לנו אירוע שמתרחש כאשר הגיעה הודעת UDP. ה delegate נראה כך:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">delegate</span> <span class="kw1">void</span> OnDatagramReceived<span class="br0">&#40;</span><span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> bytes, IPEndPoint fromThisEndPoint<span class="br0">&#41;</span><span class="sy0">;</span></div>
</div>
<p>קדימה לקוד, זה נראה ככה:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net.Sockets</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">Groundhog.Lib.MiniPatterns.Startable.ThreadSafe</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span>.<span class="me1">Net</span>.<span class="me1">Udp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> Listener <span class="sy0">:</span> ThreadSafeStartableBase, IListener<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> IPEndPoint localAddress<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> <span class="kw4">object</span> syncLock <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> <span class="kw4">object</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> UdpClient udpClient<span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> Listener<span class="br0">&#40;</span><span class="kw4">int</span> localPort<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">this</span><span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> IPEndPoint<span class="br0">&#40;</span>IPAddress.<span class="me1">Any</span>, localPort<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> Listener<span class="br0">&#40;</span>IPEndPoint localAddress<span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw1">base</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>localAddress <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;localAddress&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">localAddress</span> <span class="sy0">=</span> localAddress<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">protected</span> <span class="kw1">override</span> <span class="kw1">void</span> OnStart<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">lock</span> <span class="br0">&#40;</span>syncLock<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; udpClient <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> UdpClient<span class="br0">&#40;</span>localAddress<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; BeginReceive<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">void</span> BeginReceive<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; udpClient.<span class="me1">BeginReceive</span><span class="br0">&#40;</span>ReceiveCallback, udpClient<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">void</span> ReceiveCallback<span class="br0">&#40;</span>IAsyncResult ar<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; var uc <span class="sy0">=</span> <span class="br0">&#40;</span>UdpClient<span class="br0">&#41;</span> ar.<span class="me1">AsyncState</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; IPEndPoint remoteEndPoint <span class="sy0">=</span> null<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; var bytes <span class="sy0">=</span> uc.<span class="me1">EndReceive</span><span class="br0">&#40;</span>ar, <span class="kw1">ref</span> remoteEndPoint<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; InvokeDatagramReceived<span class="br0">&#40;</span>bytes, remoteEndPoint<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">catch</span><span class="br0">&#40;</span>ObjectDisposedException<span class="br0">&#41;</span> <br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// this one occures right after we invoke Close method</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">System.<span class="me1">Diagnostics</span></span>.<span class="me1">Debug</span>.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;swallowed the exception here&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">lock</span> <span class="br0">&#40;</span>syncLock<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Status <span class="sy0">==</span> StartableStatus.<span class="me1">Started</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BeginReceive<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">protected</span> <span class="kw1">override</span> <span class="kw1">void</span> OnStop<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">lock</span> <span class="br0">&#40;</span>syncLock<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; udpClient.<span class="me1">Close</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">event</span> OnDatagramReceived DatagramReceived<span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">void</span> InvokeDatagramReceived<span class="br0">&#40;</span><span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> bytes, IPEndPoint fromThisEndPoint<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; OnDatagramReceived received <span class="sy0">=</span> DatagramReceived<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>received <span class="sy0">!=</span> <span class="kw1">null</span><span class="br0">&#41;</span> received<span class="br0">&#40;</span>bytes, fromThisEndPoint<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>הסבר:</p>
<p>הקריאה ל OnStart יוצרת מופע חדש של UdpClient, שזו מחלקה דוט נטית.<br />
מיד לאחר מכן, יש קריאה ל BeginReceive, שזו מתודה אסינכרונית (כלומר היא תתבצע על ת'רד אחר).<br />
הכל עטוף ב lock, כי אולי, תוך כדי ביצוע תהליך ה Start, מישהו יקרא למתודת Stop, ועלולות להתקבל תוצאות בעייתיות.<br />
הקריאה ל Stop גורמת לכך שה Socket עצמו יסגר.</p>
<p>חדי העין שביניכם (שהרי רובנו גיקים עם משקפיים) בוודאי שמו לב שבתוך המתודה ReceiveCallback יש &quot;בליעה&quot; של Exception.<br />
למה זה?<br />
שאלה מצויינת: מסתבר שיש באג (מה, במיקרוסופט?? באג??) שגורם לקריאה ל Callback הנ&quot;ל גם אחרי הקריאה ל Close. לביל גייטס פתרונים. הנה תיעוד מ <a title="Bug description in Microsoft Connect" href="http://tinyurl.com/33w4z7d" target="_blank">Microsoft Connect</a>. אז אולי אפשר להימנע מה try/catch, אבל אז מתחילים להסתבך עם דגלים ועם נעילות עליהם (שהרי הקוד אמור לרוץ על יותר מת'רד אחד). לכן נראה לי הגיוני יותר להשאיר את ה try/catch הידוע לשימצה ופשוט לחיות עם זה.</p>
<h2>Start it up</h2>
<p>כמעט סיימנו. נשאר רק להציג את הקוד שמשתמש ב class הזה, ולהתחיל בבדיקות עצמן.</p>
<p>כדי להאזין להודעות UDP שמתקבלות בפורט 9777 (וזה סתם מספר שרירותי לצורך העניין) במחשב המקומי, ניצור לנו Console Application ונכתוב כך:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">ConsoleApp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw4">class</span> Program<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">&#40;</span><span class="kw4">string</span><span class="br0">&#91;</span><span class="br0">&#93;</span> args<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;starting up&#8230;&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; var listener <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Lib.<span class="me1">Net</span>.<span class="me1">Udp</span>.<span class="me1">Listener</span><span class="br0">&#40;</span>9777<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">DatagramReceived</span> <span class="sy0">+=</span> OnReceived<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;started, press ENTER to quit&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">ReadLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">static</span> <span class="kw1">void</span> OnReceived<span class="br0">&#40;</span><span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> bytes, IPEndPoint remoteEndPoint<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;Get {0} bytes from {1}&quot;</span>, bytes.<span class="me1">Length</span>, remoteEndPoint<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<h2>לבדיקה, איך נשמע?</h2>
<p>בדיקות.. אבל איך?</p>
<p>אז מה היה לנו? רכיב שמאזין להודעות UDP על פורט מסויים, ו Console Application שמחזיק את הרכיב הזה חי. איך בודקים שהסיפור הזה עובד?</p>
<div id="attachment_445" class="wp-caption alignleft" style="width: 319px"><a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/05/preh.jpg"><img class="size-full wp-image-445" title="integration test in a different age" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/05/preh.jpg" alt="" width="309" height="383" /></a><p class="wp-caption-text">בדיקת אינטגרציה בעידן אחר</p></div>
<p>אל תקפצו עם קריאות &quot;יוניט טסטינג!&quot;, כי הבדיקה של הקוד הזה היא לא ברמת ה unit, אלא ברמת ה<strong>אינטגרציה</strong> כולה.</p>
<p>כן, מסתבר ש Unit Testing זה לא תרופת פלא ולא קסם לכל דבר. ואגב, גם TDD טהור זה לא כזה גליק (ועל כך בפעם אחרת).</p>
<p>כדי לבדוק את הצד שמקבל הודעות UDP, נצטרך באמת לשלוח אותן, ולראות אם הן באמת מתקבלות. נעשה לנו בדיקה קטנה, ונראה אם כל הסיפור עובד. לא צריך עכשיו להיכנס לפינות של בדיקות עומסים, רק בדיקה שגרתית ורגילה, מה שנקרא בשפת הבודקים <a href="http://en.wikipedia.org/wiki/Smoke_testing" target="_blank">smoke-testing</a> או <a href="http://en.wikipedia.org/wiki/Sanity_testing" target="_blank">sanity-testing</a>.</p>
<p>אז צריך איזה משהו שישלח הודעות UDP למחשב מסויים ולפורט מסויים, נקרא לו UdpSender. יש לנו כאן שתי אפשרויות:</p>
<ol>
<li>אנחנו כותבים את הקוד ל UdpSender</li>
<li>מישהו אחר כתב את הקוד ל UdpSender, או שיש ממש תוכנת מדף מוכנה לעניין זה.</li>
</ol>
<p>מכיוון שמדובר בבדיקת הקוד שלנו, אופציה 1 היא פחות מוצלחת, כי אולי יש לנו תפישה שגויה לגבי משהו, שתבוא לידי ביטוי גם בכתיבת ה UdpSender. זו אחת הסיבות לכך שעדיף שקוד שנכתב ע&quot;י פלוני, יבדק ע&quot;י אלמוני.</p>
<p>אז נלך על אופציה 2. ובכן, גם כאן אפשר להתפלפל ולנסות להגיע לתוכנת מדף (ואני בטוח שיש כזו), אבל אני דווקא בחרתי לקחת code-snippet בשפת Ruby. למה Ruby? כי הקוד שם כל כך קצר (למרות שהסינטקס, איך לומר בעדינות, קצת מוזר), וגם כי נחמד להכיר. (<a href="http://en.wikipedia.org/wiki/Ruby_%28programming_language%29" target="_blank">עוד על רובי בויקיפדיה</a>)</p>
<p>הקוד ששולח את ההודעה &quot;hello&quot; ל UdpListener שלנו נראה ככה ברובי:</p>
<div class="codesnip-container" >
<div class="ruby codesnip" style="font-family:monospace;"><span class="kw3">require</span> <span class="st0">'socket'</span><br />
s = UDPSocket.<span class="me1">new</span><br />
s.<span class="me1">send</span><span class="br0">&#40;</span><span class="st0">&quot;hello&quot;</span>, <span class="nu0">0</span>, <span class="st0">'localhost'</span>, <span class="nu0">9777</span><span class="br0">&#41;</span></div>
</div>
<p>סבבצ'יק, ועכשיו, אם נריץ את כל הסיפור נקבל ב Console Application שלנו את הפלט הבא:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">starting up&#8230;<br />
started, press ENTER to quit<br />
Get 5 bytes from 127.0.0.1:52086</div>
</div>
<p>וכדי להיות בטוחים יותר שאכן חמשת הבייטים שהתקבלו הם לא שטות שרירותית, נמיר אותם לטקסט:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">private</span> <span class="kw1">static</span> <span class="kw1">void</span> OnReceived<span class="br0">&#40;</span><span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> bytes, IPEndPoint remoteEndPoint<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; var text <span class="sy0">=</span> <span class="kw5">System.<span class="me1">Text</span></span>.<span class="me1">Encoding</span>.<span class="me1">ASCII</span>.<span class="me1">GetString</span><span class="br0">&#40;</span>bytes<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;Get '{0}' from {1}&quot;</span>, text, remoteEndPoint<span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>ואכן:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">starting up&#8230;<br />
started, press ENTER to quit<br />
Get 'hello' from 127.0.0.1:59855</div>
</div>
<p>כלומר, הבדיקה שלנו מראה שההודעה אכן הגיעה ליעדה בשלום.</p>
<h1>בניית UDP Echo Server</h1>
<p>אחד היישומים הפשוטים של ה UDP Listener שלנו הוא UDP Echo Server, שזה רכיב שרץ ומאזין להודעות UDP על פורט מסויים, וכל הודעה שמגיעה &#8211; שולח בחזרה אל המקור.<br />
בשביל מה זה טוב? בעיקר כדי לבדוק תקשורת UDP (נניח, כדי לוודא שה FireWall מאפשר העברת הודעות UDP בין מחשבים, וכו'). מכיוון שזה כל כך פשוט, לא התאפקתי וכתבתי את הגירסה שלי לעניין.</p>
<p>קודם כל, הוצאתי interface מתוך ה UdpListener:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">interface</span> IListener <span class="sy0">:</span> IStartable<br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">event</span> OnDatagramReceived DatagramReceived<span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>ואח&quot;כ כתבתי את ה EchoServer כך:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net.Sockets</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">Groundhog.Lib.MiniPatterns.Startable</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span>.<span class="me1">Net</span>.<span class="me1">Udp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> EchoServer <span class="sy0">:</span> IStartable<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> IListener listener<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> UdpClient sender <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> UdpClient<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> EchoServer<span class="br0">&#40;</span><span class="kw4">int</span> port<span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw1">this</span><span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Listener<span class="br0">&#40;</span>port<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> EchoServer<span class="br0">&#40;</span>IListener listener<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>listener <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;listener&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">listener</span> <span class="sy0">=</span> listener<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">DatagramReceived</span> <span class="sy0">+=</span> DatagramReceived<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">void</span> DatagramReceived<span class="br0">&#40;</span><span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> bytes, IPEndPoint fromThisEndPoint<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>bytes <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;bytes&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>fromThisEndPoint <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;fromThisEndPoint&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; sender.<span class="me1">Send</span><span class="br0">&#40;</span>bytes, bytes.<span class="me1">Length</span>, fromThisEndPoint<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">catch</span> <span class="br0">&#40;</span>Exception<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Start<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Stop<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> StartableStatus Status<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; get <span class="br0">&#123;</span> <span class="kw1">return</span> listener.<span class="me1">Status</span><span class="sy0">;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>נעבור קצת על הקוד:</p>
<ul>
<li>הקונסטרקטור <span class="ron_english">EchoServer(IListener listener)</span> מראה לנו שכל הפונקציונליות שנדרשת מה Listener היא חיצונית ל Echo Server שלנו, ולכן &quot;מוזרקת&quot; אליו פנימה. במילים אחרות, יש כאן Dependency Injection (ובקיצור, DI). צורת העבודה הזו מאפשרת לכל מפתח שהוא ליצור Listener משלו, ולהשתמש בו בקונסטרקטור הזה. כל עוד ה Listener הנ&quot;ל יממש את ה interface שהגדרנו, הכל ימשיך לעבוד כרגיל. זה היופי של DI.</li>
<li>עם כל הכבוד ל DI, ויש כבוד, אנחנו יכולים בהחלט לחוס על המפתחים תמימי הלב ורכי הנפש שרק רוצים להריץ UDP Echo Server על פורט מסויים. לכן אנחנו יוצרים עוד קונסטרקטור ש&quot;יזריק&quot; את המימוש שכבר כתבנו ל IListener. כך נוכל לתת מענה מיידי בלי להתקשקש על הרחבות והזרקות, רק פורט להאזין בו ולסגור עניין.</li>
<li>כל קריאת start/stop/status מועברת הלאה ל IListener שיש לנו. הלוגיקה הזו כבר מוטמעת שם היטב, אין צורך להמציא אותה מחדש</li>
<li>הרכיב שמבצע את שליחת ההודעות הוא UdpClient הדוט נטי המובנה. מתכנתים שנוטים לקוד טהור יותר בהיבטים של OOP ושל SOLID יוכלו לטעון שגם זה יכול להיות מוזרק פנימה. ואני אומר, אוקיי, אפשר, אבל כמה כבר יכול להיות שונה המימוש של שליחת הודעה ב UDP? נקודה למחשבה. נגיע לזה בהמשך.</li>
<li>השאר הוא רק להאזין לאירוע של ה Listener ולשלוח בחזרה בדיוק אותה הודעה. סה טו.</li>
</ul>
<p>איך בודקים את זה? אה, זה כבר יותר מורכב, כי כאן אנחנו צריכים שני מחשבים. בגדול, מריצים את ה UDP Echo Server על מחשב X, ושיאזין על פורט P. הולכים למחשב Y, מריצים <a href="http://en.wikipedia.org/wiki/Wireshark" target="_blank">WireShark</a> (או כל network monitor אחר שיודע לנטר UDP), ושולחים הודעת UDP אל מחשב X ואל פורט P. ה WireShark כבר יראה לנו מהו פורט היציאה של ההודעה ואם התקבלה הודעה חוזרת. אני מעדיף לחסוך את תמונות המסך, תצטרכו לנסות בעצמכם או פשוט להאמין לי.</p>
<h1>בניית UDP Forwarder</h1>
<p>רכיב כמו UDP Forwarder הוא רכיב שמצד אחד מקבל הודעות UDP, ומצד שני הוא שולח אותן הלאה לכתובת אחרת. מכאן שהוא מתפקד כ Forwarder, &quot;מקדם&quot; או &quot;מקפיץ&quot; את ההודעות לכתובת אחרת.<br />
מתי שימושי? בזמנו, כשעסקנו ב Video Streaming ובפרוטוקולים כמו SIP ו RTP, יצא לנו להגיע למצב שבו המערכת היתה חייבת להזרים וידאו מכתובת מסויימת, גם אם מקור ה streaming לא היה מאותה כתובת (וזה ככה מטעמי security). אז הרצנו UDP Forwarder שכזה (ובחרנו לו שם ציורי יותר: Back to Back UDP server), שמצד אחד קיבל את הוידאו מכתובת מסויימת, ומצד שני רק המשיך את הזרמת הוידאו אל כתובת אחרת.</p>
<p>ניגש ישר לקוד, ונראה שההבדלים בין ה UDP Forwarder ל UDP Echo Server הם ממש זעירים. לא מפתיע, כי שניהם מבצעים כמעט אותה פעולה: ה Echo Server שולח את ההודעה שהתקבלה למקור שממנו הגיעה, וה Forwarder שולח את ההודעה שהתקבלה ליעד מוגדר מראש. בסה&quot;כ שני החבר'ה האלה הם ממש דומים. אולי נעשה קצת refactoring אח&quot;כ? אולי. הנה הקוד, בכל מקרה:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net.Sockets</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">Groundhog.Lib.MiniPatterns.Startable</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span>.<span class="me1">Net</span>.<span class="me1">Udp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> Forwarder <span class="sy0">:</span> IStartable<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> IListener listener<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> IPEndPoint destination<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> UdpClient sender <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> UdpClient<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> Forwarder<span class="br0">&#40;</span><span class="kw4">int</span> port, IPEndPoint destination<span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw1">this</span><span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Listener<span class="br0">&#40;</span>port<span class="br0">&#41;</span>, destination<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> Forwarder<span class="br0">&#40;</span>IListener listener, IPEndPoint destination<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>listener <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;listener&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>destination <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;destination&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">listener</span> <span class="sy0">=</span> listener<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">destination</span> <span class="sy0">=</span> destination<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">DatagramReceived</span> <span class="sy0">+=</span> DatagramReceived<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">void</span> DatagramReceived<span class="br0">&#40;</span><span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> bytes, IPEndPoint fromThisEndPoint<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>bytes <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;bytes&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; sender.<span class="me1">Send</span><span class="br0">&#40;</span>bytes, bytes.<span class="me1">Length</span>, destination<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">catch</span> <span class="br0">&#40;</span>Exception<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Start<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Stop<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> StartableStatus Status<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; get <span class="br0">&#123;</span> <span class="kw1">return</span> listener.<span class="me1">Status</span><span class="sy0">;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>ומכיוון שהדמיון כל כך חזק, אין הרבה מה לספר על הקוד, הכל מספיק ברור גם ככה. שימו לב שגם כאן יש DI, לצד הזרקת ברירת מחדל.</p>
<h1>To Refactor Or Not To Refactor</h1>
<div id="attachment_446" class="wp-caption alignleft" style="width: 307px"><a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/05/dry2.jpg"><img class="size-full wp-image-446" title="DRY" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/05/dry2.jpg" alt="" width="297" height="228" /></a><p class="wp-caption-text">Keep it DRY</p></div>
<p>יש לנו שני classים ביד, שלמעשה מבצעים כמעט אותה עבודה. כלומר יש להם הרבה מן המשותף. אחד העקרונות בתכנות הוא DRY, קרי Don't Repeat Yourself. בעברית זה יוצא &quot;אתעע&quot; <img src='http://heblog.ronklein.co.il/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> . במילים אחרות, אם יוצא לנו לכתוב קוד מאוד דומה, כדאי למצוא את המכנה המשותף ולכתוב אותו פעם אחת בלבד. למה? כי אם יש באג, אז צריך לפתור אותו בשני מקומות, וצריך בכלל לזכור שיש שני מקומות כאלה מלכתחילה. אם מתכנת זיהה באג ברכיב אחד, אין לו שום יכולת לדעת שאותו באג קיים גם ברכיב השני, כל עוד שניהם כתובים בנפרד.</p>
<p>עד כאן התביעה. ההגנה תוכל לטעון, שעם כל הכבוד ל <a title="DRY - Wikipedia" href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself" target="_blank">DRY</a>, לפעמים קוד משוכפל הוא הרבה יותר קריא, אפילו שמבחינת תחזוקה זה עלול ליצור בעיה.</p>
<p>התביעה תציין שקריאות של קוד תלויה גם בתיעוד שלו ובמי שכתב אותו. קוד גרוע הוא גרוע, בין אם הוא כתוב פעם אחת ובין אם הוא כתוב פעמיים.</p>
<p>בואו ניקח את הצד של התביעה ונעשה כאן קצת refactoring. התוצאה היא base-class (שוב ירושה, מה קורה לי בזמן האחרון?), שנראה כך:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net.Sockets</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">Groundhog.Lib.MiniPatterns.Startable</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span>.<span class="me1">Net</span>.<span class="me1">Udp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> UdpReplierBase <span class="sy0">:</span> IStartable<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> IListener listener<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> UdpClient udpClient <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> UdpClient<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> Func<span class="sy0">&lt;</span>IPEndPoint, IPEndPoint<span class="sy0">&gt;</span> destinationChooser<span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> UdpReplierBase<span class="br0">&#40;</span><span class="kw4">int</span> port, Func<span class="sy0">&lt;</span>IPEndPoint, IPEndPoint<span class="sy0">&gt;</span> destinationChooser<span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw1">this</span><span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Listener<span class="br0">&#40;</span>port<span class="br0">&#41;</span>, destinationChooser<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> UdpReplierBase<span class="br0">&#40;</span>IListener listener, Func<span class="sy0">&lt;</span>IPEndPoint, IPEndPoint<span class="sy0">&gt;</span> destinationChooser<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>listener <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;listener&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>destinationChooser <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;destinationChooser&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">listener</span> <span class="sy0">=</span> listener<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">destinationChooser</span> <span class="sy0">=</span> destinationChooser<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">DatagramReceived</span> <span class="sy0">+=</span> DatagramReceived<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">void</span> DatagramReceived<span class="br0">&#40;</span><span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> bytes, IPEndPoint fromThisEndPoint<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>bytes <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;bytes&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; var destination <span class="sy0">=</span> destinationChooser<span class="br0">&#40;</span>fromThisEndPoint<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; udpClient.<span class="me1">Send</span><span class="br0">&#40;</span>bytes, bytes.<span class="me1">Length</span>, destination<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">catch</span> <span class="br0">&#40;</span>Exception<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Start<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Stop<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> StartableStatus Status<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; get <span class="br0">&#123;</span> <span class="kw1">return</span> listener.<span class="me1">Status</span><span class="sy0">;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>ועכשיו היורשים הגאים הצטצמו פלאים, והם נראים ככה:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span>.<span class="me1">Net</span>.<span class="me1">Udp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> EchoServer <span class="sy0">:</span> UdpReplierBase<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">static</span> IPEndPoint DestChooser<span class="br0">&#40;</span>IPEndPoint x<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">return</span> x<span class="sy0">;</span> <span class="co1">// same as input</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> EchoServer<span class="br0">&#40;</span><span class="kw4">int</span> port<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">base</span><span class="br0">&#40;</span>port, DestChooser<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> EchoServer<span class="br0">&#40;</span>IListener listener<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">base</span><span class="br0">&#40;</span>listener, DestChooser<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> Forwarder <span class="sy0">:</span> UdpReplierBase<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">public</span> Forwarder<span class="br0">&#40;</span><span class="kw4">int</span> port, IPEndPoint destination<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">base</span><span class="br0">&#40;</span>port, x <span class="sy0">=&gt;</span> destination<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> Forwarder<span class="br0">&#40;</span>IListener listener, IPEndPoint destination<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">base</span><span class="br0">&#40;</span>listener, x <span class="sy0">=&gt;</span> destination<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>קומפקטי, לא? קבלנו אותה פונקציונליות, ואנחנו לא חוזרים על עצמנו (יש חזרה קטנטונת ב lamba expression שב Forwarder, אבל זה בקטנה).<br />
יתרה מכך, שמרנו בדיוק על אותם קונסטרקטורים ועל אותו API חיצוני, כך שאם כבר יש קוד כתוב &#8211; הקוד יכול להשאר כפי שהוא, בלי לשנות כלום.<br />
התביעה חוגגת, ההגנה נחה.</p>
<h1>שיפורים, שיפורים</h1>
<p>הפוסט הזה, כמו שכתבתי בתחילתו, הוא לא רק על UDP. יש כאן הרבה מעבר לפרוטוקול עצמו. נכנסתי כאן גם ל Dependency Injection, שזאת אחת השיטות לממש את ה D ב <a href="http://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29" target="_blank">SOLID</a>, והראיתי קצת Refactoring. ה UDP הוא מצע נוח יחסית להמחיש עקרונות בתכנות.</p>
<p>יחד עם זאת, יש מקומות בקוד שעוד יכולים להשתפר:</p>
<ul>
<li>כמו שציינתי קודם לכן, ה Sender יכול לקבל אבסטרקציה מלאה. אם הוא יהיה אבטרקטי, כלומר יהיה לנו interface עם מתודה אחת ויחידה כמו Send, אז נוכל גם לכתוב Unit Testing לקוד שלנו.</li>
<li>הקוד לא מתייחס עד הסוף להיבט של ת'רדים. למשל, האם ה Echo Server צריך לשלוח את ההודעה שהוא קיבל על אותו ת'רד שמטפל באירוע קבלת ההודעה? אולי כדאי לשלוח בת'רד נפרד? אם היינו כותבים את האבטרקציה ל Sender, אפשר היה לבחור במימושים שונים ל Sender: כזה ששולח על אותו ת'רד, או כזה ששולח בת'רד נפרד.</li>
</ul>
<p>אז קדימה, נמשיך עם האבטרקציה ונראה איך הכל נראה. נתחיל עם ה interface:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span>.<span class="me1">Net</span>.<span class="me1">Udp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">interface</span> ISender<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">void</span> Send<span class="br0">&#40;</span><span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> dgram, <span class="kw4">int</span> bytes, <span class="kw5">System.<span class="me1">Net</span></span>.<span class="me1">IPEndPoint</span> endPoint<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<h2>Simple Implementation</h2>
<p>נמשיך עם מימוש פשוט, ע&quot;י ה UdpClient הדוט נטי:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System.Net</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net.Sockets</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span>.<span class="me1">Net</span>.<span class="me1">Udp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> BlockingSender <span class="sy0">:</span> ISender<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> UdpClient udpClient <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> UdpClient<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Send<span class="br0">&#40;</span><span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> dgram, <span class="kw4">int</span> bytes, IPEndPoint endPoint<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; udpClient.<span class="me1">Send</span><span class="br0">&#40;</span>dgram, bytes, endPoint<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<h2>Multi-Threaded Implementation</h2>
<p>ועכשיו למימוש עם ת'רד נוסף, לצורך הפשטות &#8211; נממש בעזרת ה ThreadPool:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Threading</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span>.<span class="me1">Net</span>.<span class="me1">Udp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> ThreadPoolSender <span class="sy0">:</span> ISender<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw4">class</span> MyState<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">public</span> <span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> dgram<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">public</span> <span class="kw4">int</span> bytes<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">public</span> IPEndPoint endPoint<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> ISender blockingSender<span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> ThreadPoolSender<span class="br0">&#40;</span>ISender blockingSender<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>blockingSender <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;blockingSender&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">blockingSender</span> <span class="sy0">=</span> blockingSender<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Send<span class="br0">&#40;</span><span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> dgram, <span class="kw4">int</span> bytes, IPEndPoint endPoint<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; var myState <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> MyState<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dgram <span class="sy0">=</span> dgram,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bytes <span class="sy0">=</span> bytes,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; endPoint <span class="sy0">=</span> endPoint<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ThreadPool.<span class="me1">QueueUserWorkItem</span><span class="br0">&#40;</span>MyCallback, myState<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">void</span> MyCallback<span class="br0">&#40;</span><span class="kw4">object</span> state<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; var myState <span class="sy0">=</span> <span class="br0">&#40;</span>MyState<span class="br0">&#41;</span> state<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; blockingSender.<span class="me1">Send</span><span class="br0">&#40;</span>myState.<span class="me1">dgram</span>, myState.<span class="me1">bytes</span>, myState.<span class="me1">endPoint</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>קצת review על הקוד:</p>
<p>שימו לב שגם כאן יש Dependency Injection: אין כאן עבודה מול Sender קונקרטי, אלא מול ISender, שאמור להתקבל בקונסטרקטור. זאת משום ש<strong>האחריות היחידה</strong> של ה class הזה היא בביצוע על ת'רד אחר. כאן אנחנו נוגעים באות נוספת מה SOLID: ה S מייצגת את העקרון <strong>SRP</strong>, שזה <a href="http://en.wikipedia.org/wiki/Single_responsibility_principle" target="_blank">Single Responsibility Principle</a>.</p>
<h2>Putting it all together</h2>
<p>ועכשיו &quot;נתפור&quot; את ה ISender עם ה UdpReplierBase, שהרי זו היתה המוטיבציה לכך מלכתחילה, ונראה את התוצר הסופי:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">System.Net</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">Groundhog.Lib.MiniPatterns.Startable</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span>.<span class="me1">Net</span>.<span class="me1">Udp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> UdpReplierBase <span class="sy0">:</span> IStartable<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> IListener listener<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> ISender sender<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> Func<span class="sy0">&lt;</span>IPEndPoint, IPEndPoint<span class="sy0">&gt;</span> destinationChooser<span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> UdpReplierBase<span class="br0">&#40;</span><span class="kw4">int</span> port, Func<span class="sy0">&lt;</span>IPEndPoint, IPEndPoint<span class="sy0">&gt;</span> destinationChooser<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">this</span><span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Listener<span class="br0">&#40;</span>port<span class="br0">&#41;</span>, destinationChooser<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> UdpReplierBase<span class="br0">&#40;</span>IListener listener, Func<span class="sy0">&lt;</span>IPEndPoint, IPEndPoint<span class="sy0">&gt;</span> destinationChooser<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">this</span><span class="br0">&#40;</span>listener, <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> BlockingSender<span class="br0">&#40;</span><span class="br0">&#41;</span>, destinationChooser<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> UdpReplierBase<span class="br0">&#40;</span>IListener listener, ISender sender, Func<span class="sy0">&lt;</span>IPEndPoint, IPEndPoint<span class="sy0">&gt;</span> destinationChooser<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>listener <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;listener&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>sender <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;sender&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>destinationChooser <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;destinationChooser&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">listener</span> <span class="sy0">=</span> listener<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">sender</span> <span class="sy0">=</span> sender<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">destinationChooser</span> <span class="sy0">=</span> destinationChooser<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">DatagramReceived</span> <span class="sy0">+=</span> DatagramReceived<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">void</span> DatagramReceived<span class="br0">&#40;</span><span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> bytes, IPEndPoint fromThisEndPoint<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>bytes <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;bytes&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; var destination <span class="sy0">=</span> destinationChooser<span class="br0">&#40;</span>fromThisEndPoint<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; sender.<span class="me1">Send</span><span class="br0">&#40;</span>bytes, bytes.<span class="me1">Length</span>, destination<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">catch</span> <span class="br0">&#40;</span>Exception<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Start<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Stop<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; listener.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> StartableStatus Status<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; get <span class="br0">&#123;</span> <span class="kw1">return</span> listener.<span class="me1">Status</span><span class="sy0">;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>נוכל לכתוב למחלקות היורשות עוד קונסטרקטור. ניקח למשל את ה EchoServer שלנו:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System.Net</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span>.<span class="me1">Net</span>.<span class="me1">Udp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> EchoServer <span class="sy0">:</span> UdpReplierBase<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">static</span> IPEndPoint DestChooser<span class="br0">&#40;</span>IPEndPoint x<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">return</span> x<span class="sy0">;</span> <span class="co1">// same as input</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> EchoServer<span class="br0">&#40;</span><span class="kw4">int</span> port<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">base</span><span class="br0">&#40;</span>port, DestChooser<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> EchoServer<span class="br0">&#40;</span>IListener listener<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">base</span><span class="br0">&#40;</span>listener, DestChooser<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> EchoServer<span class="br0">&#40;</span>IListener listener, ISender sender<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">base</span><span class="br0">&#40;</span>listener, sender, DestChooser<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>כדי להפעיל את ה EchoServer שלנו ב Console Application, נוכל לכתוב כך:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">Groundhog.Lib.Net.Udp</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">ConsoleApp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw4">class</span> Program<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">&#40;</span><span class="kw4">string</span><span class="br0">&#91;</span><span class="br0">&#93;</span> args<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;starting up&#8230;&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; var echoServer <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> EchoServer<span class="br0">&#40;</span>9777<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; echoServer.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;started, press ENTER to quit&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">ReadLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; echoServer.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>אבל זו ההפעלה שלו בתצורה של ברירת מחדל. אם נרצה, למשל, ששליחת Datagrams בחזרה תתבצע על ת'רד אחר, נצטרך להזריק פנימה instance של ThreadPoolSender:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">Groundhog.Lib.Net.Udp</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">ConsoleApp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw4">class</span> Program<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">&#40;</span><span class="kw4">string</span><span class="br0">&#91;</span><span class="br0">&#93;</span> args<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;starting up&#8230;&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; var threadPoolSender <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ThreadPoolSender<span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> BlockingSender<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; var echoServer <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> EchoServer<span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Listener<span class="br0">&#40;</span>9777<span class="br0">&#41;</span>, threadPoolSender<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; echoServer.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;started, press ENTER to quit&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">ReadLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; echoServer.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<h1>מה הרווחנו בינתיים?</h1>
<p>עניין ראשון הוא שה classים שיש לנו הם יותר loosely coupled, כלומר הקטנו את הצימוד בין ה UdpReplierBase ל Sender. מה שנשאר לנו ביד זה <strong>החוזה</strong>, או <strong>ההתנהגות</strong> של ה Sender. מעכשיו, ה UdpReplierBase בכלל לא מתעניין במימוש של השליחה, הוא רק יודע שאמור להיות לו איזה רפרנס ל Sender שכזה. במילה אחת, עשינו decoupling.</p>
<p>יש לנו גם שני מימושים שונים ל ISender, הראשון פשוט (שרק עוטף את ה UdpClient הדוט נטי) והשני מורכב יותר. נוכל להשתמש בכל מימוש שהוא לפי הצורך שלנו ולפי הדרישות. כלומר השגנו הרבה גמישות תוך כדי האבסטרקציה הזו.</p>
<p>ומה עוד הרווחנו? עכשיו אפשר לעשות קצת Unit Testing ל classים שלנו (לא להכל). בקוד המצורף ניתן לראות את הטסטים עצמם, כאן בפוסט אני אציג רק טסט אחד, שהוא למעשה הטסט העיקרי ל Echo Server, כי דרכו אפשר לוודא שהקוד שכתבנו אכן יעבוד כמצופה, כל עוד המימושים של ה Listener ושל ה Sender יעבדו כראוי (וזאת המהות של unit testing &#8211; לבדוק את ה unit, ולא שום דבר אחר):</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System.Net</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">Groundhog.Lib.Net.Udp</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">Moq</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">NUnit.Framework</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span>.<span class="me1">Testing</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="br0">&#91;</span>TestFixture<span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> EchoServerTests<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>Test<span class="br0">&#93;</span><br />
&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> ReceivedMessageIsSentToSource<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; var listenerMock <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Mock<span class="sy0">&lt;</span>IListener<span class="sy0">&gt;</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; var senderMock <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Mock<span class="sy0">&lt;</span>ISender<span class="sy0">&gt;</span><span class="br0">&#40;</span>MockBehavior.<span class="me1">Strict</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; var echoServer <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> EchoServer<span class="br0">&#40;</span>listenerMock.<span class="kw4">Object</span>, senderMock.<span class="kw4">Object</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; echoServer.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> dgram <span class="sy0">=</span> <span class="br0">&#123;</span>1, 2, 3, 4, 5<span class="br0">&#125;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; var sourceEndPoint <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> IPEndPoint<span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> IPAddress<span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> <span class="kw4">byte</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="br0">&#123;</span>192, 168, 1, 5<span class="br0">&#125;</span><span class="br0">&#41;</span>, 5544<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; senderMock.<span class="me1">Setup</span><span class="br0">&#40;</span>x <span class="sy0">=&gt;</span> x.<span class="me1">Send</span><span class="br0">&#40;</span>dgram, dgram.<span class="me1">Length</span>, sourceEndPoint<span class="br0">&#41;</span><span class="br0">&#41;</span>.<span class="me1">AtMostOnce</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; listenerMock.<span class="me1">Raise</span><span class="br0">&#40;</span>x <span class="sy0">=&gt;</span> x.<span class="me1">DatagramReceived</span> <span class="sy0">+=</span> <span class="kw1">null</span>, dgram, sourceEndPoint<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; senderMock.<span class="me1">VerifyAll</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>הבדיקות נעשו ב NUnit כאשר ה Mocking Framework הוא Moq. אני לא מרחיב על עניין הבדיקות, גם ככה הפוסט הזה ארוך&#8230;</p>
<h1>מה הפסדנו?</h1>
<p>לגמישות יש מחיר: כתיבת קוד גמיש מחייבת את המשתמשים בקוד להבין לעומק את הפונקציונליות ואת ה&quot;ראש&quot; של מי שפיתח את הקוד. הגמישות יכולה בקלות להפוך לכאב ראש. גם אני, כמפתח, לפעמים רואה קוד OOP יפה ואלגנטי, ובהחלט יודע להעריך את זה, אבל תופס את הראש ושואל את עצמי &quot;מאיפה להתחיל&quot;? או, לחילופין: &quot;לא יכלו לשים class פשוט עם קונסטרקטור טיפש ולסגור עניין?!&quot;<br />
אז איך מגשרים את הפער בין גמישות ופרקטיקה?</p>
<p>קודם כל, כדאי מאוד שהגמישות תהיה לצרכים פרקטיים, ולא סתם לשם התכנות. לא לתקוע סתם עוד class כי אפשר, או כי זה נחמד שיהיה עוד. לנסות לחשוב על תרחישים שגרתיים. יתרה מכך, אם אנחנו כותבים קוד תשתיתי שישמש רק את הארגון שלנו (כלומר קוד תשתית ברמת החברה/ארגון, ולא ציבורי) &#8211; חשוב מאוד לכתוב רק קוד רלוונטי לשימושים הנוכחיים. המציאו לזה ראשי תיבות: <a title="You ain't gonna need it - Wikipedia" href="http://en.wikipedia.org/wiki/YAGNI" target="_blank">YAGNI</a>.</p>
<p>שנית, אפשר לכתוב מתודות סטטיות, כמו Create, שיוצרות instance בקלות יתרה. לדוגמה:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System.Net</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span>.<span class="me1">Net</span>.<span class="me1">Udp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">class</span> EchoServer <span class="sy0">:</span> UdpReplierBase<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co1">// code, code, code&#8230;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">static</span> EchoServer CreateMultiThreaded<span class="br0">&#40;</span><span class="kw4">int</span> port<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; var threadPoolSender <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ThreadPoolSender<span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> BlockingSender<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; var echoServer <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> EchoServer<span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Listener<span class="br0">&#40;</span>port<span class="br0">&#41;</span>, threadPoolSender<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">return</span> echoServer<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>והקריאה עצמה:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">System</span><span class="sy0">;</span><br />
<span class="kw1">using</span> <span class="co3">Groundhog.Lib.Net.Udp</span><span class="sy0">;</span></p>
<p><span class="kw1">namespace</span> Groundhog.<span class="me1">ConsoleApp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw4">class</span> Program<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">&#40;</span><span class="kw4">string</span><span class="br0">&#91;</span><span class="br0">&#93;</span> args<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;starting up&#8230;&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; var echoServer <span class="sy0">=</span> EchoServer.<span class="me1">CreateMultiThreaded</span><span class="br0">&#40;</span>9777<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; echoServer.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;started, press ENTER to quit&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">ReadLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; echoServer.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>ובאופן דומה, אפשר לכתוב <a title="Factory (software concept) - Wikipedia" href="http://en.wikipedia.org/wiki/Factory_class" target="_blank">Factory class</a> שעושה אותו הדבר.</p>
<h1>אז מה היה לנו?</h1>
<p>בפוסט הזה הראיתי איך אפשר לקחת משימה יחסית פשוטה כמו UDP Echo Server ולהדגים דרכה מספר מרכיבים בתכנות מודרני:</p>
<ul>
<li>כתיבת קוד OOP, עם מימושים שונים של Solid, ביניהם DI ו SRP.</li>
<li>Refactoring</li>
<li>Unit Testing</li>
</ul>
<p>בנוסף לכל אלה, חשוב לי לציין שלא כל קוד חייב להיות OOP טהור כבר משלב ראשון. בד&quot;כ המטרה הראשונה היא קוד שיעבוד, רק אח&quot;כ מתחילים לעבור עליו ולשכתב אותו כך שיהיה קל להבנה ולתחזוקה.</p>
<p>כמו כן, קיים הבדל משמעותי בין מספר הקבצים שצריך לתחזק (ולדבג) כאשר כותבים קוד בצורה יותר &quot;נכונה&quot;. לכתוב UDP Echo Server אפשר גם ב class יחיד. כן, אתם קוראים נכון: אחד, Uno, ואחד, one. וכאן, אחרי כל הפירוקים וההפרדות שלנו, הגענו לשבע מחלקות רק בפוסט הזה, ועוד כמה שקשורות לפנוקציונליות של Start/Stop מהפוסטים הקודמים. אז במקום class אחד יש לנו בסביבות 10 מחלקות אחרות. זה המון. האם זה מוצדק? לפעמים כן, במיוחד כאשר החלק של Unit-Testing יותר משמעותי (וחוסך בדיקות אינטגרציה בפועל), ולפעמים לא. כמו תמיד, אין תשובה אחת, וזה תלוי במידת ה code-reuse שמתקבל (ועד כמה זה באמת תורם ברמה הפרקטית), במידת הבדיקתיות, ובגורמים נוספים. לא תמיד אפשר לדעת מראש שיתקבלו כל כך הרבה מחלקות, ובכל מקרה תמיד אפשר לסדר קצת את הקוד שלנו ע&quot;י הוספת תיקיות מתאימות.</p>
<p><strong>בשורה התחתונה, כתיבת קוד OOP בגישת Solid היא מצוינת, אבל יש לה לפעמים מחיר</strong>. כמה יוצא לנו לשלם בהווה על תחזוקת קוד? וכמה אנחנו חוסכים בעתיד (כשהכל מפורק ליחידות משנה)? אלו שאלות פתוחות. עקרונות ה Solid הם עקרונות חשובים, אבל חשוב שישארו בגדר עקרונות <strong>מנחים</strong>. לא כדאי להכפיף את המציאות אליהם. לפעמים תהיה לנו מחלקה שאחראית על שני דברים (ולכן חורגת ל SRP). לשנות אותה יעלה לנו בכמה ימי עבודה. שווה לשלם את המחיר? לפעמים כן, ולפעמים לא.</p>
<p>תכנות נעים!</p>
<p>אתם מוזמנים <a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/05/udp-solid.zip">להוריד את הקוד לפוסט הזה</a> (בתצורה הסופית שלו).</p>
</div>]]></content:encoded>			<wfw:commentRss>http://heblog.ronklein.co.il/2010/05/udp-messaging-the-solid-way/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>&#8235;Startable &#8211; State Machine part 2&#8236;</title>		<link>http://heblog.ronklein.co.il/2010/04/startable-state-machine-part-2/</link>
		<comments>http://heblog.ronklein.co.il/2010/04/startable-state-machine-part-2/#comments</comments>
		<pubDate>Thu, 15 Apr 2010 22:17:34 +0000</pubDate>
		<dc:creator>&#8235;רון קליין&#8236;</dc:creator>				<category><![CDATA[תכנות]]></category>
		<category><![CDATA[Mini-Pattern]]></category>
		<category><![CDATA[Multi-Threading]]></category>
		<category><![CDATA[State Machine]]></category>

		<guid isPermaLink="false">http://heblog.ronklein.co.il/?p=395</guid>
		<description><![CDATA[&#8235;בפוסט הזה אני אמשיך ב state machine שהצגתי כבר קודם, והפעם אני אצור abstract class שיכול להיות שימושי כאשר רוצים רכיב שרץ ברקע (נניח, רכיב שמציג מניות, רכיב שמאזין לבקשות TCP וכד'). מטבע הדברים, רכיב שכזה רץ על thread משלו, והקריאה למתודת ה Start שלו, ואח&#34;כ גם ל Stop &#8211; יכולות להגיע מ threadים שונים, [...]&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p>בפוסט הזה אני אמשיך ב state machine <a href="http://heblog.ronklein.co.il/2010/03/startable-state-machine-part1/">שהצגתי כבר קודם</a>, והפעם אני אצור abstract class שיכול להיות שימושי כאשר רוצים רכיב שרץ ברקע (נניח, רכיב שמציג מניות, רכיב שמאזין לבקשות TCP וכד').<br />
מטבע הדברים, רכיב שכזה רץ על thread משלו, והקריאה למתודת ה Start שלו, ואח&quot;כ גם ל Stop &#8211; יכולות להגיע מ threadים שונים, ולכן נצטרך לדאוג לנעילות.</p>
<h3>בפשטות</h3>
<p>נתחיל מהמקרה הפשוט יותר, בלי התייחסות לענייני multi-threading.<br />
בואו נקפוץ פנימה לקוד ונסביר מה קורה:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> abstract <span class="kw4">class</span> StartableBase<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> IStartableStateMachine innerStateMachine<span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">protected</span> StartableBase<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">this</span><span class="br0">&#40;</span>StartableStatus.<span class="me1">Stopped</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">protected</span> StartableBase<span class="br0">&#40;</span>StartableStatus initialStatus<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">this</span><span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> StartableStateMachine<span class="br0">&#40;</span>initialStatus<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">protected</span> StartableBase<span class="br0">&#40;</span>IStartableStateMachine innerStateMachine<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>innerStateMachine <span class="sy0">==</span> <span class="kw1">null</span><span class="br0">&#41;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> ArgumentNullException<span class="br0">&#40;</span><span class="st0">&quot;innerStateMachine&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">innerStateMachine</span> <span class="sy0">=</span> innerStateMachine<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; innerStateMachine.<span class="me1">OnStart</span> <span class="sy0">+=</span> OnStart<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; innerStateMachine.<span class="me1">OnStop</span> <span class="sy0">+=</span> OnStop<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">protected</span> abstract <span class="kw1">void</span> OnStart<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">protected</span> abstract <span class="kw1">void</span> OnStop<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Start<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; innerStateMachine.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Stop<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; innerStateMachine.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> StartableStatus Status<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; get <span class="br0">&#123;</span> <span class="kw1">return</span> innerStateMachine.<span class="me1">Status</span><span class="sy0">;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>סקירה קצרה:<br />
יש כאן abstract class, כלומר הכוונה היא שיהיה class שיורש מה class שלנו.<br />
מי שמכיר אותי כבר יודע שאני לא מת על ירושה בקוד, אבל כאן זה מתבקש, כי זה באמת מעודד שימוש חוזר בקוד, ומצד שני די קל להבין מה הולך פה.<br />
בכל מקרה, בקלות אפשר לעקוף ירושה ע&quot;י composition, אבל לא נרחיב על זה כאן. בשביל זה יש <a href="http://www.google.com/search?q=inheritance+vs.+composition">גוגל</a>.<br />
בקיצור, ה class הזה חושף לעולם שתי מתודות: Start ו Stop.<br />
בנוסף, ב class יש קונסטרקטורים שבסופו של דבר נסמכים על state machine חיצוני, שבו נמצא כל הקוד למעברים בין המצבים.<br />
התפקיד של ה class הוא להעביר את הקריאות של ה Start וה Stop החיצוניות שלו אל ה state machine הנ&quot;ל. ההתנהגות של ה state machine, עם ה events שלו, מתגלגלת ל class היורש, באמצעות מתודות אבסטרקטיות.<br />
במילים אחרות, כל התפקיד של ה class הזה הוא לעטוף את ההתנהגות של ה state machine ולהפוך אותה ל abstract class. סבבצ'יק.</p>
<p>ניצור class חדש, שיורש מה StartableBase הנ&quot;ל, כדי לקבל את התחושה.<br />
נקרא לו MyCoolService. כי אנחנו גיקים שנותנים דוגמאות של קוּלים. זה למעשה המקסימום coolness שגיק יכול להיות, וזה רק הופך אותו ליותר גיק.<br />
ככה זה, עולם אכזר.<br />
נו, בכל אופן, הקוד:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">class</span> MyCoolService <span class="sy0">:</span> StartableBase<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">protected</span> <span class="kw1">override</span> <span class="kw1">void</span> OnStart<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;hey, now I should start working!&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">protected</span> <span class="kw1">override</span> <span class="kw1">void</span> OnStop<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;ok, now I should stop.&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>אכן, קוּל למהדרין. ככה משתמשים:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">&#40;</span><span class="kw4">string</span><span class="br0">&#91;</span><span class="br0">&#93;</span> args<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; var myCoolService <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> MyCoolService<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;myCoolService.Status = &quot;</span> <span class="sy0">+</span> myCoolService.<span class="me1">Status</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; myCoolService.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;myCoolService.Status = &quot;</span> <span class="sy0">+</span> myCoolService.<span class="me1">Status</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; myCoolService.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;myCoolService.Status = &quot;</span> <span class="sy0">+</span> myCoolService.<span class="me1">Status</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>וזו התוצאה:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">myCoolService.Status = Stopped<br />
hey, now I should start working!<br />
myCoolService.Status = Started<br />
ok, now I should stop.<br />
myCoolService.Status = Stopped</div>
</div>
<p>טוב, זה היה רק כדי לקבל תחושה, זה לא <strong>באמת</strong> קול.</p>
<h3 style="unicode-bidi: bidi-override; direction: ltr ! important;">Threads Ahoy!</h3>
<p>זה מתחיל להיות מעניין כשעובדים עם יותר מ thread אחד.<br />
אז גם כאן אולי כדאי שנתחיל מ abstract class, שמתבסס על הקודם (בירושה! סלח לי אבי כי חטאתי!), ונקפוץ לקוד:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="co1">/// &lt;summary&gt;</span><br />
<span class="co1">/// Marks the implementation as a thread-safe one. </span><br />
<span class="co1">/// Nothing more than that.</span><br />
<span class="co1">/// &lt;/summary&gt;</span><br />
<span class="kw1">public</span> <span class="kw4">interface</span> IThreadSafeStateMachine <span class="sy0">:</span> IStartableStateMachine<br />
<span class="br0">&#123;</span><br />
<span class="br0">&#125;</span></p>
<p><span class="kw1">public</span> abstract <span class="kw4">class</span> ThreadSafeStartableBase <span class="sy0">:</span> StartableBase<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">protected</span> ThreadSafeStartableBase<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">:</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">this</span><span class="br0">&#40;</span>StartableStatus.<span class="me1">Stopped</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">protected</span> ThreadSafeStartableBase<span class="br0">&#40;</span>StartableStatus initialStatus<span class="br0">&#41;</span> <span class="sy0">:</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">this</span><span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> StartableStateMachineInterlock<span class="br0">&#40;</span>initialStatus<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">protected</span> ThreadSafeStartableBase<span class="br0">&#40;</span>IThreadSafeStateMachine innerStateMachine<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">base</span><span class="br0">&#40;</span>innerStateMachine<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>סה&quot;כ מעטפת ל abstract class שכבר היה לנו, עם תוספות קלות.<br />
כדי להשתמש בכל הסיפור הזה, צריך רק לרשת ולהתחיל לעבוד עם קצת נעילות.<br />
בדוגמה הבאה, יש לנו class שבכל X שניות (או כל TimeSpan שנקבע) מבצע פעולה מסויימת, נניח משהו כמו פינג.<br />
בנוסף, יש לנו Counter שמונה את הפינגים שבוצעו. הקוד:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">class</span> MyThreadSafeService <span class="sy0">:</span> ThreadSafeStartableBase, IDisposable<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> TimeSpan period<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> <span class="kw4">object</span> syncLock<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">readonly</span> Timer timer<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw4">int</span> counter <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> MyThreadSafeService<span class="br0">&#40;</span>TimeSpan period<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">this</span><span class="br0">&#40;</span>period, <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> <span class="kw4">object</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> MyThreadSafeService<span class="br0">&#40;</span>TimeSpan period, <span class="kw4">object</span> syncLock<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">:</span> <span class="kw1">base</span><span class="br0">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> StartableStateMachineSimpleLock<span class="br0">&#40;</span>StartableStatus.<span class="me1">Stopped</span>, syncLock<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">period</span> <span class="sy0">=</span> period<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">syncLock</span> <span class="sy0">=</span> syncLock<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; timer <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Timer<span class="br0">&#40;</span>MyTimerCallback<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">void</span> MyTimerCallback<span class="br0">&#40;</span><span class="kw4">object</span> state<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> current<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">lock</span> <span class="br0">&#40;</span>syncLock<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; counter<span class="sy0">++;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; current <span class="sy0">=</span> counter<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;Ping! so far {0} pings&quot;</span>, current<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw4">int</span> Counter<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; get<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">lock</span> <span class="br0">&#40;</span>syncLock<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> counter<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">protected</span> <span class="kw1">override</span> <span class="kw1">void</span> OnStart<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; counter <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; timer.<span class="me1">Change</span><span class="br0">&#40;</span>TimeSpan.<span class="me1">Zero</span>, period<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">protected</span> <span class="kw1">override</span> <span class="kw1">void</span> OnStop<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; timer.<span class="me1">Change</span><span class="br0">&#40;</span>TimeSpan.<span class="me1">FromMilliseconds</span><span class="br0">&#40;</span><span class="sy0">-</span>1<span class="br0">&#41;</span>, TimeSpan.<span class="me1">FromMilliseconds</span><span class="br0">&#40;</span><span class="sy0">-</span>1<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Dispose<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Stop<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; timer.<span class="me1">Dispose</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>הקריאה:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">&#40;</span><span class="kw4">string</span><span class="br0">&#91;</span><span class="br0">&#93;</span> args<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; var period <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> TimeSpan<span class="br0">&#40;</span>0, 0, 1<span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// ping every second</span><br />
&nbsp; &nbsp; var myThreadSafeService <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> MyThreadSafeService<span class="br0">&#40;</span>period<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; myThreadSafeService.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; Console.<span class="me1">ReadLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; myThreadSafeService.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;Counter = &quot;</span> <span class="sy0">+</span> myThreadSafeService.<span class="me1">Counter</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; myThreadSafeService.<span class="me1">Dispose</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>והתוצאה (אחרי בערך 5 שניות):</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">======== Thread Safe Example ===========<br />
Ping! so far 1 pings<br />
Ping! so far 2 pings<br />
Ping! so far 3 pings<br />
Ping! so far 4 pings<br />
Ping! so far 5 pings</p>
<p>Counter = 5<br />
=========== End of Example =============</p>
<p>press &#8211;enter&#8211; to quit</p></div>
</div>
<p>וואלה עובד.</p>
<h3>סיכום</h3>
<p>מי שכותב מדי פעם רכיב שפועל ברקע, כנראה מצא את עצמו חוזר על קוד התשתית של &quot;להתחיל משהו, לדאוג לנעילות, לסנכרן סטטוס&quot; וכו'.<br />
בשני הפוסטים האלה הראיתי איך אפשר לכתוב base class שמעודד שימוש חוזר בקוד מהסוג הזה.<br />
היתרון העיקרי הוא שאפשר להפריד בין המנגנון הפנימי של ה state machine וגם, באופן חלקי, לנעילות המתבקשות.<br />
מצד שני, הירושה יכולה לגרום לכאב ראש כשנכנסים לדיבאג (או כשסתם מנסים להבין את זה).<br />
בנוסף, יש לנו לא מעט קבצים לתחזק, רק כדי לכתוב קוד &quot;נכון&quot;.<br />
מה המסקנה? לא תמיד יש מסקנה חד משמעית, צריך לנסות ולראות אם זה מתאים לכם.<br />
הערות והארות, או סתם תגובות יתקבלו בברכה. ובכלל, מי שקרא עד הלום &#8211; סחתן, כה לחי וישר כוח <img src='http://heblog.ronklein.co.il/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>אפשר <a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/04/startable-part-2.zip">להוריד את כל הקוד</a>, כמובן.</p>
</div>]]></content:encoded>			<wfw:commentRss>http://heblog.ronklein.co.il/2010/04/startable-state-machine-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8235;A Few Words on StackOverflow.com&#8236;</title>		<link>http://heblog.ronklein.co.il/2010/04/a-few-words-on-stackoverflow-com/</link>
		<comments>http://heblog.ronklein.co.il/2010/04/a-few-words-on-stackoverflow-com/#comments</comments>
		<pubDate>Tue, 06 Apr 2010 23:00:27 +0000</pubDate>
		<dc:creator>&#8235;רון קליין&#8236;</dc:creator>				<category><![CDATA[תכנות]]></category>
		<category><![CDATA[site-review]]></category>
		<category><![CDATA[stackoverflow.com]]></category>

		<guid isPermaLink="false">http://heblog.ronklein.co.il/?p=354</guid>
		<description><![CDATA[&#8235;האתר stackoverflow הוא אתר מרכזי לכל מפתח תוכנה. באמת הכל דבש? אולי יש גם כמה מילות ביקורת?&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><div id="attachment_376" class="wp-caption alignleft" style="width: 260px"><a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/04/stack_logo.png"><img class="size-full wp-image-376" title="stackoverflow.com logo" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/04/stack_logo.png" alt="" width="250" height="61" /></a><p class="wp-caption-text">הלוגו של stackoverflow</p></div>
<p>הפוסט הזה הוא על האתר <a href="http://stackoverflow.com" target="_blank">stackoverflow.com</a> (<a href="http://http://en.wikipedia.org/wiki/Stackoverflow" target="_blank">ויקיפדיה</a>), אתר שהפך להיות אחד המרכזיים והפעילים בתחום פיתוח תוכנה וכל מה שמסביב, בסגנון של מישהו-שואל-שאלה-ומלא-אנשים-עונים-עליה. או, בניסוח שלהם: Stack Overflow is a programming Q &amp; A site that's free. איך זה שהאתר הזה הפך להיות כל כך פופולרי תוך זמן קצר? ואולי, לצד ההצלחה, יש לו גם כמה צדדים פחות טובים?</p>
<p>נתחיל מההצלחה עצמה: האתר הפך להיות ה-אתר ה-מוביל בעולם בשאלות/תשובות בענייני תכנות, וכל זאת כמעט מיד עם השקתו. האתר כולל מערכת ניקוד עשירה (שמזכירה פה ושם את המתחרה שנשאר הרחק מאחור, <a title="Experts Exchange" href="http://www.experts-exchange.com/" target="_blank">experts-exchange</a>), ונכון להיום, הוא ב<a href="http://www.alexa.com/siteinfo/stackoverflow.com" target="_blank">מקום 545 לפי מדד אלקסה</a>. כבוד.</p>
<h2>אז מה כל כך טוב ב stackoverflow הזה?</h2>
<h3>נו, זה פשוט נראה טוב</h3>
<p>ממשק משתמש (כן, כן, אני אמור לרשום &quot;מנשק משתמש&quot;, אבל בחייכם, &quot;מנשק&quot;?!) נקי, מאורגן, ומהיר. התחושה היא שאין יותר מדי זבל, ואפילו הפרסומות בטוב טעם.</p>
<h3>כן, וגם חוויית משתמש טובה</h3>
<p>אין ספק, החבר'ה ב stackoverflow עשו עבודה טובה בהיבט של User Experience, ובכל הקשור לתגובתיות ולמהירות הניווט &#8211; זה פשוט עובד, ועובד נפלא. השימוש ב ajax הופך את העניינים לפחות &quot;תהליכיים&quot; והכל זורם יותר טוב (לדוגמה &#8211; הצמדת תגים לשאלה חדשה זה עניין ממש פשוט, גם כאשר מדובר בתג חדש).</p>
<h3>אה כן, אין שם קטגוריות, הכל תגיות</h3>
<p>בשנים האחרונות חל מפנה בארגון המידע, ובמקום היררכיה של קטגוריות (נניח קטגוריית &quot;מסדי נתונים&quot; ושם תתי-קטגוריה של &quot;Oracle&quot; ו&quot;MS SQL Server&quot;), ב stackoverflow יוצרים תגיות. אמנם, גם &quot;תגית&quot; וגם &quot;קטגוריה&quot; מסווגים את המידע ומאפשרים סינון, אלא שמידע מבוסס תגיות הוא שטוח, ואין צורך להסתגל להיררכיית סיווג מידע שחשב עליה עורך תוכן או מידען כזה או אחר. אם נרצה, למשל, לצפות בשאלות שעוסקות אך ורק ב SMTP וב Oracle &#8211; נוכל לסנן לפי התגיות הרלוונטיות וזהו. לא נצטרך להתחקות אחר הקטגוריות המובנות. ב stackoverflow הבינו את זה, ולקחו את זה עד הסוף, גם נוח וגם יעיל. אגב, גם אתרים כמו themarker הישראלי הרימו את הכפפה של תגיות.</p>
<h3>ויש מערכת מוניטין עשירה (Voting and Reputation)</h3>
<p>אני חושב שזה ה&quot;לב&quot; של הפעילות ב stackoverflow. מערכת המוניטין והתגמול היא ממש עשירה. שאלת שאלה &#8211; יש עליה הצבעות (votes) אם היא טובה ומעניינת (או שלא, ואז התגמול הוא שלילי). ענית תשובה &#8211; כנ&quot;ל.<br />
מה שנחמד בכל הסיפור הוא שתשובות עם הצבעות גבוהות עליהן &quot;צפות&quot; ו&quot;גוברות&quot; על תשובות אחרות. וזה גם מה שלא נחמד בסיפור הזה, חכו תקראו.</p>
<p>זה לא נגמר רק במוניטין של נקודות נטו. יש גם מערכת badgeים: מעין &quot;תגים&quot;, או &quot;עיטורי כבוד&quot; לפעילות כזו או אחרת. שאלת שאלה שיש עליה יותר מ 10 הצבעות חיוביות &#8211; קיבלת את ה&quot;עיטור&quot; שנקרא Nice Question. שאלת שאלה שזכתה ל 2500 צפיות ומעלה &#8211; קיבלת את ה&quot;עיטור&quot; שנקרא Notable Question. ועוד ועוד.</p>
<p><strong>אל תשלו את עצמכם</strong>, זה עובד על כולם. אנחנו בני אדם, וכולנו פועלים ומופעלים ע&quot;י רגש (ומי שטוען אחרת, שיסביר לי מה זאת אהבה). כולנו נהנים לקבל תגמול חיובי, מי יותר ומי פחות. ואיכשהו, שם ב stackoverflow, המינון הוא כזה שזה לא מגוחך ולא על כל שטות מקבלים איזה עצם, ומצד שני, על דברי טעם מקבלים חיזוקים. התוצאה היא שמי שמתמיד ומשתתף, ולא מקשקש סתם תשובות &#8211; מתוגמל די טוב.</p>
<h3>יש שם המון המון (המון!) ידע. באמת המון</h3>
<div id="attachment_378" class="wp-caption alignleft" style="width: 230px"><a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/04/Hulk_work.jpg"><img class="size-full wp-image-378" title="ידע הוא כוח?" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/04/Hulk_work.jpg" alt="" width="220" height="121" /></a><p class="wp-caption-text">stackoverflow?</p></div>
<p>מתכנתי כל העולם התאחדו! אם ידע הוא כוח, הרי ש stackoverflow הוא לפחות הענק הירוק. קחו שאלה כמו <a style="unicode-bidi: bidi-override; direction: ltr !important;" href="http://stackoverflow.com/questions/9033/hidden-features-of-c" target="_blank">Hidden Features of C#</a> ותראו כמה ידע מזוקק אפשר לקבל מהרבה אנשים שכנראה מעולם לא הכרתם אישית.</p>
<h3>יש היענות</h3>
<p>שאלות של טעם מקבלות מענה רב (ובד&quot;כ גם מקבלות הצבעות חיוביות), ושאלות מעניינות באמת זוכות לכל כך הרבה תשובות, שלפעמים לא ברור אם כל המתכנתים הפעילים בעולם עצרו את עבודתם והחליטו לענות. עד כדי כך.</p>
<h3>ויש גם קצת הומור פה ושם</h3>
<div id="attachment_380" class="wp-caption alignleft" style="width: 138px"><a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/04/620ed2c3129f0c0887efbc5a54d6125c.jpg"><img class="size-full wp-image-380" title="My gravatar on April Fools" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/04/620ed2c3129f0c0887efbc5a54d6125c.jpg" alt="" width="128" height="128" /></a><p class="wp-caption-text">ה-gravatar שלי?</p></div>
<p>יום אחד, למשל, הוסיפו לינק מוזר של קשת בענן לכל שאלה (עם הכיתוב <em>Cornify</em>). כאילו בארבי בעצמה החליטה לתת חסות לאתר. לחיצה על הלינק המוזר גרמה להופעת תמונה בסגנון של קשת בענן, חד קרן, ירח וכד'. אחרי כמה לחיצות רצופות היתה תחושה שבארבי ובראץ חברו יחד (!) כדי להשתלט על הגיקים שמבקרים ב stackoverflow: המסך התמלא בתמונות קיטש מזעזעות. ואז, אחרי עוד לחיצה או שתיים, הופיעה הודעת &quot;אחד באפריל&quot; <a title="תמונת מסך" href="http://aprilfoolsdayontheweb.com/joke/7197/?size=1" target="_blank">על המסך</a>. חמוד, מצחיק, וגם, מה לעשות, קצת גיקי <img src='http://heblog.ronklein.co.il/wp-includes/images/smilies/icon_wink.gif' alt=':wink:' class='wp-smiley' /> . כל זה היה באחד באפריל 2009. השנה (2010) המתיחה היתה יותר צנועה, ורק שינו את ה <a title="מה זה gravatar?" href="http://en.wikipedia.org/wiki/Gravatar" target="_blank">gravater</a> של כולם להיות בסגנון של חד-קרן-קשת-בענן-לבבות.</p>
<p>נו, אז אם הכל כל כך טוב ויפה, אז מה יכול להיות רע?</p>
<h2>אז נעבור לדברים&#8230; הלא כל כך טובים</h2>
<h3>זה תזזיתי</h3>
<p>כמות השאלות הנשאלות בסטאק בחמש דקות היא אדירה. נסו בעצמכם להגיע לדף הראשי, להסתכל על שאלה שנשאלה רק עכשיו, ולרפרש את הדף. אם השאלה לא מעניינת את האנשים באתר, ולא זוכה לפעילות בדקותיה הראשונות באוויר &#8211; היא נדחקת לתחתית הדף. השאלות החדשות דוחקות אותה למטה, ולאחר זמן קצר תמצאו את השאלה בדפים פנימיים יותר (כלומר לא בדף הראשי). התוצאה העגומה היא שאם לא התקבלו תשובות מספיק טובות לשאלה נתונה תוך זמן קצר (שעה לכל היותר) &#8211; זה, כנראה, גם המצב הסופי שלה.<br />
אז מצד אחד, יש שם המון ידע. כלומר המון אנשים תורמים מהידע שלהם וכמעט בטוח שלכל שאלה נורמלית יש תשובה. ומצד שני, יש שאלות שלא מקבלות תשובות מספיק טובות. כלומר אין מתאם בין כמות הידע לבין איכות התשובות, בעיקר בגלל חשיפה נמוכה מדי של שאלות.<br />
אגב, כדי לפצות על כך, האתר מספק מנגנון של bounty questions למי שרוצה לקבל חשיפה ארוכה יותר, אבל זה כבר סיפור אחר.</p>
<h3>הניקוד יכול להטעות</h3>
<p>אורך חיי השאלה בסטאק הוא קצר. מי שמחפש מוניטין ורואה שאלה חדשה מהניילונים &#8211; יענה עליה, ויענה <strong>מהר</strong>. למה מהר? כדי להיות העונה הראשון (או השני, או בין הראשונים), וע&quot;י כך לקבל חשיפה ארוכה יותר לתשובה, שתוביל לניקוד חיובי.<br />
מצד שני, לוקח זמן לכתוב תשובה טובה ומפורטת.<br />
וזה האבסורד: תשובה מהירה באיכות בינונית מקבלת חשיפה ארוכה יותר, שמובילה (או לפחות יכולה להוביל) לניקוד גבוה. תשובה באיכות גבוהה, שמופיעה מאוחר יותר &#8211; מקבלת חשיפה קצרה יותר ולכן גם הניקוד עליה יכול להיות נמוך יותר. לפעמים יש תשובות קצרצרות שמקבלות הרבה הצבעות חיוביות (הנה דוגמה  משלי &#8211; 5 הצבעות חיוביות <a href="http://stackoverflow.com/questions/1173154/fileupload-asp-net/" target="_blank">לתשובה של שורה אחת</a>). לפעמים להיפך &#8211; תשובות מושקעות  יכולות לקבל כינים, רק כי השאלה כבר עברה את זמן החשיפה שלה.</p>
<div id="attachment_386" class="wp-caption alignleft" style="width: 148px"><a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/04/kitbag.png"><img class="size-full wp-image-386" title="שאלות קיטבג" src="http://heblog.ronklein.co.il/wp-content/uploads/2010/04/kitbag.png" alt="" width="138" height="220" /></a><p class="wp-caption-text">stackoverflow?</p></div>
<h3>שאלות קיטבג</h3>
<p>לפעמים אני נתקל בשאלות שהן על סף הטריוויאליות, ונראה שהן שם רק כדי שהשואל יוכל לקבל ניקוד ומוניטין. למעשה, שיטת המוניטין באתר היא כזו שמי שיש לו מוניטין של פחות מ 15 נקודות &#8211; לא יכול לחוות דעתו על שאלות ותשובות. זה קצת מבאס למי שרוצה להשתתף ולקחת חלק פעיל כבר בהתחלה. כדי לקצר תהליכים, יוצא שאנשים עם מוניטין נמוך נוטים להמציא שאלות שהן לא תורמות הרבה לאנושות, אלא בעיקר עם פוטנציאל לתרום לשואל כמה נקודות.</p>
<h2>אז מה נסגר?</h2>
<p>טוב, זה עדיין נחשב להיות ה-אתר ה-מוביל של שאלות ותשובות בענייני תכנות. בעקבות ההצלחה שלו נפתחו אתרים דומים בתחומים אחרים (למשל, <a href="http://serverfault.com/" target="_blank">server-fault</a> בתחום ה IT), או אתרים בהשראתו, מוצלחים יותר או פחות (למשל, <a href="http://www.iask.co.il/" target="_blank">iask</a> הישראלי).</p>
<p>עם הצלחה לא מתווכחים. ועדיין, למרות שהאתר עובד יפה, ויש בו הרבה ידע, הטרייד-אוף שלו הוא תזזיתיות וזמן חשיפה קצר מאוד לשאלות. חשיפה כל כך קצרה גורמת למצב שבו שאלות טובות יכולות להשאר ללא מענה (או עם מענה חלקי בלבד), כשברור שיש לאנשים את הידע והיכולת לענות.<br />
העולם אכזר גם בשאלות ובתשובות טכניות, מסתבר.</p>
<p>נתראה!<br />
<script src="http://stackoverflow.com/users/flair/17772.js?theme=clean" type="text/javascript"></script></p>
</div>]]></content:encoded>			<wfw:commentRss>http://heblog.ronklein.co.il/2010/04/a-few-words-on-stackoverflow-com/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>&#8235;SMS Integration: Israel is wayyy behind US&#8236;</title>		<link>http://heblog.ronklein.co.il/2010/03/sms-integration-israel-is-wayyy-behind-us/</link>
		<comments>http://heblog.ronklein.co.il/2010/03/sms-integration-israel-is-wayyy-behind-us/#comments</comments>
		<pubDate>Sat, 13 Mar 2010 23:18:26 +0000</pubDate>
		<dc:creator>&#8235;רון קליין&#8236;</dc:creator>				<category><![CDATA[אינטגרציה]]></category>
		<category><![CDATA[תכנות]]></category>
		<category><![CDATA[Integration]]></category>
		<category><![CDATA[SMS]]></category>

		<guid isPermaLink="false">http://heblog.ronklein.co.il/?p=323</guid>
		<description><![CDATA[&#8235;ספקי אינטגרציה לשליחת SMS מקומיים לעומת ספק אמריקאי - מי יותר טוב? ניחשתם נכון, בואו תבינו למה...&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p>והפעם פוסט סביב אינטגרציה.</p>
<p>החלטנו להוסיף למוצר שלנו מערכת התראות ב SMS. הנמענים אמורים להיות בישראל, בלי מגבלה על הרשתות והמפעילים.<br />
האינסטינקט הראשוני הוא ליצור קשר עם ספקי SMS בישראל, שמאפשרים התממשקות באמצעות Web Service כזה או אחר, להתרשם ולקנות חבילה.<br />
גיגלתי ומצאתי שני ספקים ישראליים:</p>
<ul>
<li>SimpleSMS</li>
<li>שירות של גולדמן תקשורת שנקרא SMS API</li>
</ul>
<p><span style="font-weight: bolder;">התרשמתי מ SimpleSMS לטובה</span>. <a target="_blank" href="http://www.simplesms.co.il/">האתר שלהם</a> נראה נחמד, יש תיעוד טוב ודוגמאות קוד לשפות תכנות רבות, גם לדוט נט. ואז רציתי לשלם. אחרי מילוי מושקע של הפרטים, קבלתי הודעת שגיאה לא ברורה עם מסך של טרנזילה. ועכשיו אני בבעיה: היה חיוב או לא היה חיוב?<br />
בצר לי, התקשרתי. מכירים את המוקדים הטלפוניים האלה, שעונים לכם אנשים חינניים שבכלל לא קשורים לעסק עצמו, אלא רק שם כדי לקבל שיחות ולהעביר את הקריאה הלאה? כן כן, זה מה שהיה שם, ואמרו שיתקשרו אלי תוך 45 דקות. אחרי חצי שעה ניסיתי שוב, והדגשתי שזו הודעה שניה. כל זה היה לפני שבוע בערך, ואני עדיין מחכה.<br />
אז אם אין שירות סביר ל SimpleSMS, אין טעם שאני אשאר איתם. מזל שיש מתחרים.</p>
<p><span style="font-weight: bolder;">אז כאמור, גולדמן תקשורת</span>. שם זה סיפור אחר: באתר שלהם מדברים על API בצורת Web Service, ואפילו יש screenshot שממחיש את זה, אבל תיעוד אמיתי אין. ודוגמאות קוד, זה בכלל &quot;מוקצה&quot;. האמת, חייבים <a target="_blank" href="http://www.goldman.co.il/SMSAPI/smsapibiz/">להיכנס ולראות</a> כדי להאמין.<br />
אבל אני, אופטימיסט אני. אמרתי, ניתן צ'אנס, נרים טלפון. התקשרתי ובקשתי לנסות את המוצר, או לפחות לקבל את ה API הנכסף (ולא ב screenshot). מהעבר השני של הקו הסביר לי, בתקיפות לא ברורה, שהם היחידים שעובדים דרך סלקום, כבר שנים, ושהמתחרים לא אמינים כמותם. הסיטואציה לא היתה מספיק ברורה (לשנינו, כנראה), וסיכמנו שכדאי לי לדבר עם אסף, הבן שלו, בעניין ה API.<br />
מסתבר שתחום ה pre sales בגולדמן תקשורת קצת בעייתי, כי אסף סרב לספק את התיעוד ל API, והסביר שאני צריך לקנות את השירות כדי לקבל את ה API והתיעוד. עכשיו, נכון שהחבילה המינימלית עולה בסה&quot;כ 20 ש&quot;ח, וזה סכום פעוט לחברות תוכנה, אבל למה להחביא את ה API? מה כל כך סודי בו? אסף הסביר לי שמנסיון העבר שיש להם, זו מדיניות החברה.<br />
יש משהו קצת פישי בהסתרת API, ולא בעיה להרים שירות Dummy שיהווה &quot;מתקן אימונים&quot; אם רוצים להסתיר את הכתובת של השירות האמיתי. במילים אחרות, אני לא רואה בעיה שהיא לא פתירה, ובגלל זה אני מסיק שיש כאן בעיה של מקצועיות.<br />
אז גם גולדמן תקשורת נפסל.</p>
<p><span style="font-weight: bolder;">באותו זמן, ומהעבר השני של החדר</span>, כלומר במרחק של מטר וחצי ממני, <a target="_blank" href="http://reshefmann.com/blog/">רשף</a> כבר נרשם לשירות אמריקאי שנקרא <a target="_blank" href="http://www.twilio.com/">Twilio</a>. הם שולחים הודעות SMS להרבה מקומות בעולם, גם לישראל. תוך חצי שעה כבר נרשמנו, שילמנו (עם סליקה מוצלחת!), והיה לנו קוד מלא ועובד.</p>
<p>נחזור רגע לאינסטינקט הראשוני (מתחילת הפוסט), שאומר שבשביל שירות אינטגרציה מקומי כדאי לפנות לספקים מקומיים, כי הם בטח מתמצאים יותר ויהיו דרכם פחות בעיות טכניות. מסתבר שהפעם, פניה לספק חיצוני העלתה תוצאה טובה יותר: האמריקאים כנראה יודעים לעשות את זה טוב יותר ומקצועי יותר מהישראלים (ובכמות גדולה של הודעות, גם זול יותר). אולי כי הסליקה שלהם עובדת בלי בעיה, ואולי כי הכל פשוט מתקתק כמו שצריך, עם שירות מבוסס ReST, ועם תיעוד מלא ומקיף (ולא מוסתר&#8230;).<br />
זה אולי לא מדגם מייצג, אבל בכל זאת, בשנת 2010, הייתי מצפה מחברות טכנולוגיה/אינטגרציה מקומיות להרים שירות בסטנדרט גבוה יותר.</p>
<p>לא נורא, העיקר שעובד <img src='http://heblog.ronklein.co.il/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
</div>]]></content:encoded>			<wfw:commentRss>http://heblog.ronklein.co.il/2010/03/sms-integration-israel-is-wayyy-behind-us/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>&#8235;Startable &#8211; State Machine part 1&#8236;</title>		<link>http://heblog.ronklein.co.il/2010/03/startable-state-machine-part1/</link>
		<comments>http://heblog.ronklein.co.il/2010/03/startable-state-machine-part1/#comments</comments>
		<pubDate>Thu, 11 Mar 2010 20:15:02 +0000</pubDate>
		<dc:creator>&#8235;רון קליין&#8236;</dc:creator>				<category><![CDATA[תכנות]]></category>
		<category><![CDATA[Mini-Pattern]]></category>
		<category><![CDATA[State Machine]]></category>

		<guid isPermaLink="false">http://heblog.ronklein.co.il/?p=203</guid>
		<description><![CDATA[&#8235;מכונת מצבים פשוטה מאוד (מופעל/מופסק), שיכולה להיות שימושית כשמפתחים "יישומי רקע" (חלק 1)&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p>בפוסט הזה:</p>
<ul>
<li>קצת על State Machine</li>
<li>Startable State Machine</li>
<li>מתי משתמשים</li>
<li>מימוש רגיל</li>
<li>הפרדת interface מהמימוש</li>
<li>מימוש עם נעילות</li>
<li>הדגמה פשוטה</li>
</ul>
<p><a href="http://heblog.ronklein.co.il/2010/04/startable-state-machine-part-2/">בפוסט הבא</a> אני אראה איך אפשר להשתמש ב state machine הזה (בירושה, למשל)</p>
<h2>קצת על State Machine</h2>
<p>חלק משמעותי מעולם התכנות כולל מעבר ממצב x למצב y, כאשר כל המצבים ידועים מראש. אפשר לומר שכל התוכניות שכתבתי עד היום הן כאלו. אמנם,לא תמיד ידעתי לצפות מראש את כל המצבים, ופה ושם היו באגים שבדרכם גרמו לי להבין שיש עוד מצב או שניים שצריך לקחת בחשבון, אבל בסופו של דבר, זו היתה קבוצה סגורה ומוגדרת היטב של מצבים.</p>
<p>קבוצת המצבים הזו יכולה להיות מתוארת כמכונת מצבים (סופית), או באנגלית Finite State Machine, ובקיצור FSM. המעבר ממצב למצב נקרא transition ובד&quot;כ הצורך לעבור ממצב אחד לאחר נובע מקלט כזה או אחר, או הודעה פנימית. אפשר לקרוא עוד בויקיפדיה (<a href="http://he.wikipedia.org/wiki/%D7%90%D7%95%D7%98%D7%95%D7%9E%D7%98_%D7%A1%D7%95%D7%A4%D7%99" target="_blank">עברית</a> ו<a href="http://en.wikipedia.org/wiki/State_machine" target="_blank">אנגלית</a>) כדי להעמיק.</p>
<h2>Startable State Machine</h2>
<p>תרשו לי להציג מכונת מצבים פשוטה ונחמדה. אולי פשוטה מדי, אבל אני מקווה שאצליח לשכנע שהיא נחוצה ומעודדת שימוש חוזר בקוד (שזו מטרה נאה לכל הדעות). אני קורא למצבים פשוטים כאלה &quot;mini patterns&quot;. לא ממש design pattern, אבל בהחלט מעודד שימוש חוזר בקוד.</p>
<p>מדובר בסה&quot;כ בשני מצבים אפשריים: Started ו Stopped. או בעברית: מופעל/מופסק.</p>
<p>נדמיין שני לחצנים: לחצן Start ולחצן Stop (כמו Play ו Stop במכשיר DVD, למשל)</p>
<p>אם המצב הנוכחי הוא Stopped, ולחצנו על Start &#8211; עוברים למצב Started. כל לחיצה נוספת על Start לא משנה את המצב.</p>
<p>וכנ&quot;ל להיפך: אם המצב הנוכחי הוא Started, ולחצנו על Stop &#8211; עוברים למצב Stopped. כל לחיצה נוספת על Stop לא משנה את המצב.</p>
<p>לצורך העניין, מצב הפתיחה הוא Stopped.</p>
<p>התרשים הבא יכול לתאר את זה:</p>
<p><img src="http://heblog.ronklein.co.il/wp-content/uploads/2010/02/startable_sm.png" alt="Startable State Machine - chart" /></p>
<h2>מתי משתמשים?</h2>
<p>השימוש העיקרי שלי ב Startable הוא כשבתוך אפליקציה קיימת, אני צריך איזשהו שירות שיהיה פעיל ברקע כל הזמן, או פעיל לפעמים. למשל: שרת UDP שמאזין להודעות נכנסות בפורט מסויים, סטרימר ששולח Streaming Video למחשב אחר, רכיב שבודק כל X שניות מה הסטטוס של רכיב אחר, וכו' וכו'. לכל רכיב שכזה יש בד&quot;כ שני מצבים: &quot;פעיל&quot; או &quot;לא פעיל&quot;. קלאסי ל Startable.</p>
<h2>מימוש רגיל</h2>
<p>המימוש הוא די פשוט:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw4">enum</span> StartableStatus<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Started,<br />
&nbsp; &nbsp; &nbsp; &nbsp; Stopped<br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">public</span> <span class="kw4">class</span> StartableStateMachine<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">private</span> StartableStatus status<span class="sy0">;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">event</span> Action OnStart<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">event</span> Action OnStop<span class="sy0">;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">public</span> StartableStateMachine<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw1">this</span><span class="br0">&#40;</span>StartableStatus.<span class="me1">Stopped</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">public</span> StartableStateMachine<span class="br0">&#40;</span>StartableStatus initialStatus<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; status <span class="sy0">=</span> initialStatus<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">void</span> InvokeAction<span class="br0">&#40;</span>Action e<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw1">null</span> <span class="sy0">==</span> e<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; e<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">void</span> InvokeOnStop<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; InvokeAction<span class="br0">&#40;</span>OnStop<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">private</span> <span class="kw1">void</span> InvokeOnStart<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; InvokeAction<span class="br0">&#40;</span>OnStart<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Start<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>status <span class="sy0">==</span> StartableStatus.<span class="me1">Started</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return<span class="sy0">;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// it was &quot;stopped&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// change to &quot;started&quot; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; status <span class="sy0">=</span> StartableStatus.<span class="me1">Started</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// and raise the proper event</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; InvokeOnStart<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Stop<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>status <span class="sy0">==</span> StartableStatus.<span class="me1">Stopped</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return<span class="sy0">;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// it was &quot;started&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// change to &quot;stopped&quot; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; status <span class="sy0">=</span> StartableStatus.<span class="me1">Stopped</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// and raise the proper event</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; InvokeOnStop<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">public</span> StartableStatus Status<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; get<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> status<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> &nbsp; &nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>סקירת קוד קצרה:</p>
<ul>
<li>יש כאן enum על הסטטוס: Started או Stopped</li>
<li>אפשר לקבל את הסטטוס הנוכחי באמצעות המאפיין Status (דה!)</li>
<li>בנוסף יש שתי מתודות: Start ו Stop שהן ה&quot;לב&quot; של ה state machine שלנו</li>
<li>וכדי להודיע לעולם שהסטטוס השתנה, יש שני אירועים: OnStart ו OnStop.</li>
</ul>
<p>מה אין כאן?<br />
הקוד הוא לא thread-safe, מיד נגיע לזה.</p>
<h2>הפרדת interface מהמימוש</h2>
<p>ממוש, תפריד לי את ה interface מהמימוש, טוב?<br />
הנה, מותק:</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">namespace</span> Groundhog.<span class="me1">Lib</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">public</span> <span class="kw4">interface</span> IStartableStateMachine<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">event</span> Action OnStart<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">event</span> Action OnStop<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">void</span> Start<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; StartableStatus Status <span class="br0">&#123;</span> get<span class="sy0">;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="kw1">void</span> Stop<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>ועכשיו, אחרי שיש לנו הפרדה בין החוזה למימוש, אפשר לממש בצורות אחרות.</p>
<h2>מימוש עם נעילות</h2>
<p>כדי שהקוד יהיה thread-safe, אפשר:</p>
<ul>
<li>להשתמש בנעילות רגילות (או באבסטרקציה שהצעתי <a href="http://heblog.ronklein.co.il/2010/01/from-lock-to-abstract-locker/">בפוסט אחר</a>)</li>
<li>לעבוד עם Interlock, שמהווה מנגנון יותר light-weight לנעילות מהסוג שצריך כאן</li>
</ul>
<p>כל הקוד לנעילות השונות נמצא <a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/03/Groundhog.zip">בקובץ המצורף</a>. אני לא נכנס לזה פשוט כי זה לא העיקר בפוסט הזה.</p>
<h2>הדגמה פשוטה</h2>
<p>הנה קוד של Console Application שנותן לנו קצת להרגיש את כל מה שהיה לנו עד עכשיו.</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">using</span> <span class="co3">Groundhog.Lib</span><span class="sy0">;</span><br />
<span class="kw1">namespace</span> Groundhog.<span class="me1">ConsoleApp</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw4">class</span> Program<br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">&#40;</span><span class="kw4">string</span><span class="br0">&#91;</span><span class="br0">&#93;</span> args<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; IStartableStateMachine ssm <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> StartableStateMachine<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">OnStart</span> <span class="sy0">+=</span> MyOnStart<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">OnStop</span> <span class="sy0">+=</span> MyOnStop<span class="sy0">;</span></p>
<p>&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;===========================&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;simple flow: start and stop&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;===========================&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>ssm.<span class="me1">Status</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>ssm.<span class="me1">Status</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>ssm.<span class="me1">Status</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;========================================&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;multiple starts and then multiple stops,&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;should invoke each event just once&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;========================================&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">Start</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>ssm.<span class="me1">Status</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; ssm.<span class="me1">Stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span>ssm.<span class="me1">Status</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;===================&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;press enter to quit&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;===================&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">ReadLine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">static</span> <span class="kw1">void</span> MyOnStart<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;hey, it started!&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">static</span> <span class="kw1">void</span> MyOnStop<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; Console.<span class="me1">WriteLine</span><span class="br0">&#40;</span><span class="st0">&quot;dude, this thing stopped&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>והפלט יהיה:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">===========================<br />
simple flow: start and stop<br />
===========================<br />
Stopped<br />
hey, it started!<br />
Started<br />
dude, this thing stopped<br />
Stopped</p>
<p>
========================================<br />
multiple starts and then multiple stops,<br />
should invoke each event just once<br />
========================================<br />
hey, it started!<br />
Started<br />
dude, this thing stopped<br />
Stopped</p>
<p>
===================<br />
press enter to quit<br />
===================</div>
</div>
<p>כפי שניתן לראות, גם אחרי מספר קריאות עוקבות ל start, הסטטוס נשאר started, וכנ&quot;ל גם לגבי קריאות למתודה stop. כלומר יש לנו state machine בדיוק כמו שרצינו.</p>
<p><a href="http://heblog.ronklein.co.il/2010/04/startable-state-machine-part-2/">בהזדמנות הבאה</a> אני אראה איך משתמשים בקוד הזה בירושה <del datetime="2010-04-15T22:04:00+00:00">(ובדרך נוספת)</del>, בדיוק עבור אותם רכיבים שמבצעים איזושהי עבודה ברקע.</p>
<p>תכנות נעים!</p>
<p>כל הקוד בפוסט הזה <a href="http://heblog.ronklein.co.il/wp-content/uploads/2010/03/Groundhog.zip">נמצא כאן</a>.</p>
</div>]]></content:encoded>			<wfw:commentRss>http://heblog.ronklein.co.il/2010/03/startable-state-machine-part1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
