SpinUntil Optimization

בפוסט הקודם, ראינו שימוש ב SpinUntil ע"מ לבדוק האם תנאי מסויים מתקיים תוך פרק זמן מוגדר. ראינו גם שהבדיקה של התנאי מתרחשת בתדירות גבוהה – מה שיכול להביא לעומס של המערכת ולגרום לעיכוב בתוצאות בגלל עומס יתר, ולא בגלל שהתנאי לא היה מספיק להתקיים תוך כדי הזמן.

ניתן ליעל את השימוש ב SpinUntil ע"י שנבדוק את התנאי כל פרק זמן ארוך יותר.

שוב, נכתוב פונקציה אשר בודקת שתנאי מסויים מתקיים, או שעובר פרק זמן. הפונק' תקבל פרמטר של זמן, וגם מצביע לפונק' שמחזירה ערך בוליאני.

כעת: כמה זמן נמתין בין בדיקה לבדיקה? אפשר לקבוע ערך ברירת מחדל, לדוגמא נמתין בין שני בדיקות 500 מילי, אך מה אם נרצה לבדוק שהתנאי מתקיים עד 500 מילי?

אם כך נקבע ערך מינימאלי מסויים, לדוגמא כל 50 מילי נבדוק את התנאי, אך במקרה שאנו מראש מבקשים לוודא שהתנאי מתקיים עד חצי דקה, שם נוכל להגדיל את מרווח הזמן בין כל בדיקה.

const int DefultNumberOfcheck = 100;
const int OneCycleMinimumSleepMsec = 50;
public void WaitUntil(TimeSpan timeout, Func<bool> checkCondition)
{
var sleepTime = getSleepTime(timeout);
Stopwatch sw = Stopwatch.StartNew();
while (true)
{
Assert.Less(sw.Elapsed, sleepTime);
if (checkCondition())
{
return;
}
Thread.Sleep(sleepTime);
}
}
private TimeSpan getSleepTime(TimeSpan timeout)
{
var sleepTimeoutmilisec = (int)(timeout.TotalMilliseconds / DefultNumberOfcheck);
return TimeSpan.FromMilliseconds(Math.Max(sleepTimeoutmilisec, OneCycleMinimumSleepMsec));
}

Unit Test With SpinUntil

כאשר אנו כותבים Unit Tests יש לנו מקרים שאנו רוצים לוודא שתנאי מסויים מתממש לפני שאנו מסיימים את הבדיקה. כך שאנו גם נסכים לחכות לו זמן מה.
לדוגמא, לאחר ביצוע משיכת כספים מחשבון הבנק, נבדוק כי היתרה התעדכנה במחשב המרכזי עד 5 שניות מרגע המשיכה.
נוכל להשתמש במתודה SpinUntil שממתינה עד שנקבל ערך true או עד פקיעת timeout.

var spinRes = SpinWait.SpinUntil(waiter.IsRecievedAck, 20000);

חשוב לשים לב כי התנאי, או הפונק' שבודקת את התנאי תופעל המון פעמים. על כל מחזור של המעבד. לפעמים אין לנו בעיה עם זה, אבל ישנם מקומות שאנו לא רוצים להפעיל את אותה בדיקה המון פעמים, אלא רוצים להגדיר מרווח זמן בין כל בדיקה.

בפוסט הבא נראה אופטימיזציה על Spin Untill

התרופה הפשוטה "לחסימה" בכתיבת בלוג

מזה זמן רב שלא פרסמתי אף לא פוסט אחד. לא שלא היו לי נושאים לכתוב עליהם, פשוט – לא יודע איך להסביר את זה.

לפעמים אני מתלבט, האם הפוסט קצר או ארוך מדאי, האם צריך להסביר בצורה מפורטת יותר, או עדיף להסביר ממש בקצרה. בכל מקרה, כל הדברים האלו מובילים אותו לדבר אחד – לכתוב ממש מעט, או יותר נכון לכתוב המון טיוטות – ולא לפרסם מאומה.

בשיחה עם חבר טוב, ואיש מוערך, ינון פרק, הופנתי לפוסט הזה. הנקודה החשובה שאני רוצה לקחת מהפוסט הזה – פשוט להתחיל לכתוב. הדיוק בהסבר, או אורך הפוסט – יגיעו בהמשך. אך מה שחשוב הוא – להניע את הגלגלים.

Wpf Converters vs StringFormat

ישנם כמה דרכים כדי להציג נתונים ב wpf,

נקח דוגמא פשוטה שאפשר לממש אותה בעזרת שתי הדרכים:

Leading Zeroes: אנו נדרשים ליצג מספר בעזרת כמה אפסים שמובילים את הספרות האמיתיות. נניח כי אנו רוצים להציג 4 אפסים "0000" כך ששכל ספרה שהמשתמש יכניס – תרגום למחיקת 0 אחד, והחלפתו במספר.

  1. נוכל לממש במצעות Converter, כך שכל שינוי באותו שדה, יבצע פעולה של "ריפוד" השדה ב n אפסים (נקבל את המשתנה הזה דרך פרמטר שנעביר ל converter ב xaml). – הכל יעבוד כאן כמו שצריך, אך אנו יצרנו converter חדש, להתעסק עוד קצת ב xaml כדי ליצור אותו וסתם כך להעמיס על המערכת.

2.StringFormat שיוכל להכתב xaml.זה ממש להוסיף כמה מילים – ממש פשוט.

 <TextBlock Text="{Binding myText , StringFormat=Code : {0:D4}}" />

ל Converter יש המון כח, אפשר לעשות בקוד איזו מניפולציה שנרצה, וכך להציג את הנתונים בצורה שרצויה לנו. אולם יש לקחת בחשבון שאנו מעמיסים על המערכת עם converters, ביצירת מופעים ובתחזוקה של קוד. כאשר ישנם דבררים פשוטים, בלי לוגיקה מיוחדת – אפשר להשתמש StringFormat.

מאמר מומלץ בנושא תוכלו לקרוא כאן

לומדים לתכנת עם הילדים

קצת רקע…

לפני שנה, ראיתי באתר של משרד החינוך הזמנה לילדים להשתתף באליפות הסייבר. באתר הוצעו כמה פלטפורמות של לימוד, כל אחת מיועדת לשכבה אחרת של גילאים.

באותו זמן, משרד החינוך איפשר ללמוד בחינם דרך קודמאנקי. נכנסתי דרך ההזדהות של הילד שלי, והתרשמתי ממש לחיוב. האתר מלמד את העקרונות הבסיסיים דרך משחק. יש קוף שצריך לעזור לו לאכול את הבננות.

ההרגשה היא כמו במשחק, בהתחלה החוויה מאוד נעימה, השלבים קלים, עד שמתקדמים עוד שלב ועוד שלב, לומדים דברים קצת יותר מתקדמים (פונקציות וכו') ומרגישים שהמשחק הוא כבר לא 'משחק'.

המון עידוד

כמו שלנו לא היה כ"כ פשוט בקורס 'מבוא למשעי המחשב' שבמבט לאחור לא למדנו שם משהו ממש מורכב, סה"כ למדנו את הבסיס… הקושי היה אפוא, לימוד והפנמה של העקרונות.

גם כאן, הילד לומד לחשוב 'אלגוריתמים'. איך ליעל את הקוד ואיך לחשוב 'לולאות'. להבין בצורה מוחשית שחייב להשתמש כאן ב if. else , לוגיקה ואירועים.

לדעתי להורה יש כאן תפקיד מכריע בהצלחה של הלימוד. כמו בכל לימוד מעמיק ומתקדם. מילה טובה, לשבת יחד ולהסביר פעם ועוד פעם – תורמים בצורה משמעותית.

כמה זמן להשקיע?

לדעתי גם שעה בשבוע זה מעולה. העיקר משהו על בסיס קבוע. העקרונות שנלמדים שם צריכים להיות מופנמים ו 'להלעס' היטב. יש חשיבות מרובה לתרגול, גם לשבת ולחזור על חומר שלמדנו כבר – זה דבר מבורך.

צריך להבין שבעיקרון, הילד שלנו רוצה לעשות דבר אחד – לשחק! כאן אנחנו מלמדים אותו חומר מאוד כבר, גם אם אנחנו עושים את זה בצורת משחק… זה בדיוק כמו לתת לו לפתור תרגילים קשים בחשבון בצורה חוויתית. החומר כאן הוא חומר העשרה, והלימוד צריך להעשות בנעימות ובכיף. (אם כי לדעתי, צריך להושיב את הילדים בצורה אקטיבית ללמוד. אפשר לומר עכשיו אנחנו לומדים יחד.)

חלוקת השלבים

ישנם כמה שלבים, כל שלב הוא לימוד של נדבך אחר.

כל שלב במשחק מורכב משני חלקים.

1. חלק ההרפתקאות, שם לומדים את העקרונות. בכל שלב לומדים, עם הקוף, עוד אספקט אחר. השלבים לא קשים כ"כ


2. חלק האימון, שם מתרגלים (גם עם הקוף), את מה שלמדנו בשלבים של ההרפתקאות. רק ברמה קצת יותר גבוהה. אני סבור שכדאי ממש להתאמץ ולא לוותר על אף תרגול. זה מה שיעזור להפנים את החומר.

איך ללמוד ?

אני חושב שכדאי קודם להסתכל על המשימה, לבקש מהילד לקרוא את ההוראות ולא לרוץ יש לקוד. לחשוב בקול, בעברית, במילים פשוטות. מה המטלה ואיך אנחנו מתכוננים לפתור אותה.
"נעבור על כל הבננות הצהובות ולא הירוקות". כעת אפשר לומר, נרוץ עם for על הבננות, ועל כל בננה נבדוק עם if האם הבננה not green…
חשוב מאוד שהילד ידע לקרוא קוד. אפשר לשאול, תסביר לי את הקוד בשורה 1 2 3.
חשוב גם להבין מאיפה התוכנית רצה. האם הוא מבין שבסוף הלולאה חוזרים לתחילת הלולאה, האם הוא מבין שכעת מבצעים פונקציה.
לא תאמינו כמה ילדים יצירתיים. הם יכולים לפתור לא מעט שלבים, בלי להבין באמת מה הם עשו. פשוט עשיתי, זה עבד וקיבלתי 3 כוכבים…

3 כוכבים..

Unlocking Files with Sysinternals Process Explorer

כאשר אנו עובדים מול קובץ, למעשה אנו מחזיקים מצביע למבנה הנתונים של הקובץ. כך שאנו יכולים לערוך אותו וכו'. כך למעשה יש לתוכנית שלנו גישה לאותו קובץ (יותר נכון – לכל אוביקט). בסיום העבודה עם אותו משאב, אנו צריכים לסגור את אותו מצביע, קישור לקובץ, או בשם האמיתי handle.

לפעמים, אנו נתקעים ולא מצליחים למחוק, לשמור או לשנות קובץ, משום שכבר יש handle פתוח לאותו קובץ, שעדיין לא נסגר. במקרה זה אנו צריכים לאתר את אותה תוכנית שמחזיקה handle, ולבדוק האם הקובץ שלנו פתוח עדיין באותה אפליקציה. אולם יש מקרים שעדיין יהיה קשה לנו להבין מי 'תופס' את אותו קובץ שלנו.

ישנם כמה דרכים להבין איזה proces מחזיק handle לקובץ שלי. בפוסט זה נראה איך עובדים עם Proces Explorer.

Process Explorer הוא כלי מעולה, שמשמש לחקור את התהליכים שרצים במערכת שלנו. הוא כלי הרחבה ל Task Manager. ויכול להחליף אותו בצורה מובנית.

לצורך הדוגמא, נחקור מקרה בו Word מחזיק handle לקובץ בשם AmielHandle.

נוכל להבין למה הקובץ שלנו 'נעול' באחד משני דרכים.

  1. אם אנו יודעים כבר איזה תהליך 'נועל' לנו את הקובץ, נוכל לראות לאילו משאבים הוא מחזיק hadle, ואז לשחרר את אותו handle ספציפי.
  2. נוכל לחפש איזה תהליך מחזיק לנו בקובץ ע"י שנחפש את שם הקובץ .
לכידת התהליך ע"י כלי הלכידה
חיפוש תהליך ע"פ handle

תבנית עיצוב – Repository

רקע

נניח כי אנו כותבים אפליקציה שמקבלת נתונים מכמה מקורות במקביל, מנתחת אותם, ומקבלת החלטות ע"פ לוגיקה מסויימת.

  1. Web Service – מקור אחד יכול להיות web service שמאחסן נתונים בפורמט JSON.
  2. CSV – מקור אחר הוא קובץ csv שמאוחסן על המחשב שלנו.
  3. SQL – מקור נוסף הוא בסיס נתונים SQL מרוחק.
  4. בהמשך חיי המוצר יתווספו עוד כמה סוגים של פורמטים

הרעיון המשותף הוא שיש לנו כמה מקורות מידע שונים, שנבדלים גם במיקום שלהם, וגם בפורמט שבהן הנתונים שמורים, אנו נשאף שהקוד שצורך את הנתונים האלו, לא יהיה מושפע בשום צורה מסוג הנתונים וצורת ההתחברות.

במילים אחרות אנו רוצים לתכנן צורה של הפרדה בין הקוד שמטפל בלוגיקה של הנתונים, כי בסופו של דבר אנו מסתכלים אל כל מקור נתונים כאל אוסף רשומות, לבין הפורמט של הנתונים. האפליקציה שלנו לא צריכה לדעת איך פותחים חיבור לבסיס הנתונים, או כותבים שאילתות SQL, איך ניגשים לקבצים, או איך ניגשים למקורות נתונים ולפורמטים אחרים שעדיין לא חשבנו עליהם כעת.

הרעיון

כדי לבצע את ההפרדה, נחשוב איזו פונקציונאליות האפליקציה שלנו צריכה ולא משנה מאיזה מקור נתונים. לדוגמא, אם אנו עוסקים עם נתונים, סביר להניח שנרצה לתמוך ב CRUD (Create, Read,Update,Delete).

  1. ניצור ממשק שישקף את הפונקציונאליות שמשותפת לכל סוגי אחסון הנתונים.
  2. לכל סוג אחסון, תהיה מחלקה שמממשת את הממשק המשותף. במחלקה זו יהיה המימוש הספציפי שמתאר את צורת ההתחברות ואת הטיפול בסוג הנתונים המיוחדים האלו. (את האמת, גם את הפעולה הזו אפשר להפריד לעוד סוג של ממשק, אבל נעשה זאת בהמשך)
  3. בקוד של האפליקציה נקבל את הנתונים, ונעבוד איתם דרך הממשק שכתבנו, רק שבפועל המימוש שלו יהיה שונה בהתאם למקור הנתונים שאיתו נרצה לעבוד.

התבנית הזו היא ממש פשוטה ונפוצה – פשוט לכתוב ממשק שדרכו נוכל לעבוד עם כל המימושים.

בתרשים הנ"ל אנו רואים ממשק שמאפשר קבלת לוגים. מצד ימין ושמאל אנו רואים את המחלקות שמממשות את אותו ממשק, והם בלבד יודעים איך לקרוא צורות מסוימות של לוגים. הממשק שמפריד את המימוש מההתנהגות נקרא בשם Repository/

את הקוד המלא, תוכלו לראות כאן

MVVM Desigen Patten

MVVM היא תבנית עיצוב מאוד נפוצה, בעיקר כאשר עובדים עם ui. כאשר אנו כותבים פרויקט גדול, אנו נדרשים לחלק את העבודה בין הצוותים, ולהפריד את האחריות שיש לכל צוות באופן הברור ביותר ולשאוף לאי תלות.

כאשר אנו מדברים על צד שרת, וצד לקוח – ההפרדה בניהם נראית ברורה ומחייבת. אך גם כאשר אנו מדברים על שכבת ה UI בלבד, אנו נדרשים לשאול את השאלה: האם הקוד הזה שייך למימוש הספציפי של החלון שלי כעת (לדוג. wpf), אך במקרה של מימוש Web, האם יהיה ניתן יהיה להשתמש בקוד הזה? אם ניתן להשתמש בקוד הזה – הדבר אומר כי יש להפריד את אותה לוגיקה למחלקה אחרת. אך מנגד, יהיו דברים שהם ספציפים למימוש הנוכחי, לדוגמא הכפתור ב wpf, שונה מהכפתור ב web. (ב wpf אנו כותבים את החלון ב xaml , ו ב web אנו כותבים ב html). אבל אם נחשוב על מקרה בו אם הטמפרטורה בחוץ גבוהה, הכפתור , יוצג באדום, ואם הטמפרטורה נמוכה – נציג את הכפתור בירוק. הקוד הזה לא קשור לצורת המימוש, והוא יוכל לרוץ בכל מימוש של תצוגה. לכן את הלוגיקה הזו נכתוב במחלקה שנפרדת מהחלון, נקרא לה viewModel, "הלוגיקה של החלון". השאיפה היא "לקשור" את הצבע של הכפתור, ללוגיקה שמחושבת ב viewModel.

בדוגמא להלן אנו רוצים להציג על המסך אוסף של שורות שנכתבו ללוג. אם אני אכתוב את המימוש שלי ב wpf, אני ארצה ארצה לעבוד עם ListBox, אך כאשר ארצה לעבוד עם web, אעבוד עם פקד אחר שהוא לר בהכרח ListBox וצורת ההפעלה שלו היא אחרת. אך עד שאציג את הרשימה לאוביקט של ה ui, אצטרך לעשות בדיוק את אותם פעולות. לאחזר את הרשומה מאותו repository, לקבל אותו כ enumerable, ורק אז להציג אותו למסך.

אם כן נוכל לחלק את המשימה שלנו לשורות הבאות

  1. אחזור רשומת הלוג מהשירות – משותף לכל סוגי המימושים
  2. עבודה עם list או enumerable – משותף לכל סוגי המימושים
  3. אולי למיין את הרשימה לפני שמציגים למשתמש – משותף לכל סוגי המימושים
  4. קישור הרשימה לאוביקט ui – צריך להכתב בנפרד עבור כל סוג מימוש

בדיוק עבור זה נועדה תבנית העיצוב mvvm, להפריד את הלוגיקה (שורות 1-3) מהמימוש הספציפי של ה ui. הרעיון לכתוב את המימוש של הלוגיקה, זה שיהיה משותף לכל סוגי ה ui, במחלקה נפרדת בשם ViewModel, ואת החלק שקשור ממש ל ui נכתוב בחלק אחר שנקרא View.

דוגמא נוספת,

אם נרצה להציג למשתמש את מזג האוויר שיש היום, כך שאם הטמפרטורה גבוהה מ 25 מעלות, נציג את המספר באדום, ואם הטמפרטורה קטנה מ 25 מעלות, נציג את המספר בצבע ירוק. נפרט את השלבים

  1. ניגש לשירות מזג האוויר ונבקש את הטמפרטורה כעת.
  2. נחליט באיזה צבע להציג את הנתונים.
  3. נכתוב את הנתונים ל ui, עם הצבע הנדרש.

ברור לנו שהשרת לא אמור להכיר שיש הבדל בצבע בין טווחי הנתונים, ובוודאי שאם מחר אנו נחליף את ה ui לממשק של web או לממשק של mobile, לא נכתוב את הלוגיקה הזו מחדש. הלוגיקה הזו תהיה בשכבת הביניים שלנו, בשכבה שקבלה את הנתונים מהשרת, מבצעת עליהם לוגיקה, ומעבירה אותם ל ui. וזה בדיוק View Model.

בפרויקטים גדולים, יש המון לוגיקה שקשורה לאופן תצוגת הנתונים ב ui, עוד מבלי לעסוק ב איך ה ui בנוי. לשם כך נרצה לכתוה unit test עבור חלק הלוגיקה של ה ui. כאשר אנו מפרידים את חלק הלוגיקה למחלקות נפרדות שלא קשורות לאוביקטים ספציפיים של החלון, אנו מצליחים לכתוב בדיקות בצורה פשוטה יותר, מבלי להיות מוכרחים ליצור mocks של החלון ושאר הפקדים של ה ui.

יותר לעומק…

ההפרדה שדיברנו עליה בפוסט הזה, עושה עבודה טובה, ואכן מפרידה את הלוגיקה של ה ui מהצימוד ההדוק למימוש התצוגה בעזרת WPF , ASP וכו'. אך עדיין בקוד שהצגתי, הקישור של ה View Model ל DataContext של החלון, התבצע ב MainWindow.cs, . הדבר הזה הכריח את המימוש שלי ב WPF , לדעת מהו הטיפוס ViewModel, וחייב אותו לבצע רק את ה API שמוגדר בממשק של ILogViewModel. הצורה הנכונה יותר היא לקשר את ה vieewModel ל DataContext דרך ה xaml, כך שההיכרות בינהם תהיה הכירות של אוביקט כללי, object, ולא אובייקט מטיפוס ספציפי כמו כאן. (על אך שגם פה אנחנו יכולים לבצע כל לוגיקה שהיא מהטיפוש של ILogViewModel, אך עדיין ישנו צימוד לסוג הממשק הזה בלבד… )

 public partial class MainWindow : Window
    {
        ILogViewModel vm;
        
        public MainWindow(ILogViewModel viewModel)
        {
            InitializeComponent();
            vm = viewModel;
            this.DataContext = vm;
        } 
        private void GetRepository_Click(object sender, RoutedEventArgs e)
        {
            vm.CreateLogRepository();
        } 
        private void ClearData_Click(object sender, RoutedEventArgs e)
        {
            vm.Clear();
        }
    }

את קוד המקור, תוכלי לראות כאן

IMemoryCache .Net

כאשר אנו מבצעים פעולות שלוקחות הרבה זמן, ואותם נתונים לא אמורים להשתנות בתדירות גבוהה, אנו נרצה לחסוך את הפניה בחוזרת לשירות המרוחק כל פעם שנרצה לקבל את אותם נתונים.

לדוגמא כאשר נרצה לקבל נתונים של מזג אוויר, נוכל לחסוך את הפניה של כל משתמש לשירות מזג האוויר, אם משתמש אחר כבר ביקש את התחזית לאותו מקום בשעה האחרונה.

הרעיון ב cache הוא לשמור את הנתונים בזיכרון של התוכנית, כך שאם נבקש את אותם נתונים תוך זמן מסויים, לא נגש לשירות המרוחק, דבר שיארך זמן, אלא נקח את הנתונים מהזיכרון של התוכנית, שזמין לנו מיידית.

מיקרוסופט שיחררה מימוש של Cache שהוא גם Tread-Safe וגם תומך במדיניות פינוי רשומות ישנות.

אילו סוגי מדינויות פינוי יש לנו?

  1. Absolute Expiration – הבלוק יפונה לאחר זמן מוגדר מראש.
  2. Sliding Expiration – הבלוק יוסר רק אם לא ניגשו אליו תוך פרק זמן מסויים.
  3. Size Limit – הגבלה של מספר הרשומות שניתן לאחסן.

ראשית יש לגשת ל Nuget כדי להתקין את הספריה של Microsoft.Extensions.Caching.Memory , ניתן לבחור באיזה פרויקט להתקין.

הורדה מ Nuget והתקנה בפרויקט
הורדה מ Nuget והתקנה בפרויקט

נראה את השימוש בספריה ע"י דוגמא,

יש לנו קוד אשר מאחזר רשומות לוג. הרשומות יכולות להיות מאוחסנות במקום שזמן הגישה אליו הוא ארוך, ולכן אנו רוצים לכתוב מנגנון שבכל פניה ישמור את כל הרשומות בזיכרון. אך אם בין בקשה לבקשה עבר זמן מועט ביותר, אז אנו לא נפנה שוב לבסיס הנתונים, אלא יאחזר את הרשומות מה cache.

namespace LogRepository.CacheDecorator
{
    public class CacheDecorator : ILogRepository
    {
        ILogRepository m_logRepository;
        MemoryCache m_cache;
        private readonly double absoluteExpirationTime = 30;

        public CacheDecorator(ILogRepository logRepository)
        {
            m_logRepository = logRepository;
            m_cache = new MemoryCache(new MemoryCacheOptions() { SizeLimit = 1024 });

        }
        public IEnumerable<LogMessage> GetLogs()
        {
            //if record exit in cache -return from cache.
            //else - feath from remote origin. 

            var key = "LogEntry";
            IEnumerable<LogMessage> cacheEntry = null;
            if (!m_cache.TryGetValue(key, out cacheEntry))// Look for cache key.
            {
                // Key not in cache, so get data.
                cacheEntry = m_logRepository.GetLogs();

                var cacheEntryOptions = new MemoryCacheEntryOptions()
                    .SetSize(1)
                    .SetAbsoluteExpiration(TimeSpan.FromSeconds(absoluteExpirationTime));

                // Save data in cache.
                m_cache.Set(key, cacheEntry, cacheEntryOptions);
            }
            return cacheEntry; 
        }
    }
}

הסבר מפורט

את הקוד המלא ניתן למצוא כאן

Decorator Design Pattern

בפוסט זה נסקור תבנית עיצוב חשובה מאוד. בתבנית זו נשתמש כאשר נרצה להרחיב או לשנות התנהגות של יחידה מסויימת, מבלי לשנות את אותה היחידה המקורית.

לדוגמא, אם אנו מקבלים נתונים ממקור מרוחק, כך שלוקח זמן עד שכל השאילתא מתבצעת. כעת, נרצה לשפר במעט את ההתנהגות, כך שאם לא חלפו 5 דקות מרגע השאילתא הקודמת, במקרה זה, לא נפנה לשירות לקבל את הנתונים, אלא נשתמש בנתונים שקיבלנו השאילתא האחרונה, שאת הנתונים האלו נאחסן בזיכרון.

הרעיון הוא לכתוב מחלקה נוספת שמממשת את הממשק הקיים, המחלקה הזו תגדיר בתוכה את ההתנהגות שהיא רוצה לעשות את הנתונים (במקרה שלנו – לבדוק האם הנתונים התקבלו לאחרונה ואז לא לבקש אותם שוב מהשרת) אך ב Constructor אנו נקבל את הטיפוס של הממשק הראשי, כך שנוכל להוסיף את הפונקציונאליות שלנו עבור כל מימוש שקיים. ומכאן השם Decorator, לקחת מימוש קייים, שחברה אחרת כתבה, שאנחנו לא משנים אותו, ולבצע את הלוגיקה שאנו רוצים.

 public class CacheDecorator : ILogRepository
    {
        ILogRepository m_logRepository;
        MemoryCache m_cache;
        private readonly double absoluteExpirationTime = 30;

        public CacheDecorator(ILogRepository logRepository)
        {
            m_logRepository = logRepository;
            m_cache = new MemoryCache(new MemoryCacheOptions() { SizeLimit = 1024 });

        }
        public IEnumerable<LogMessage> GetLogs()
        {
            //if record exit in cache -return from cache.
            //else - feath from remote origin. 

            var key = "LogEntry";
            IEnumerable<LogMessage> cacheEntry = null;
            if (!m_cache.TryGetValue(key, out cacheEntry))// Look for cache key.
            {
                // Key not in cache, so get data.
                cacheEntry = m_logRepository.GetLogs();

                var cacheEntryOptions = new MemoryCacheEntryOptions()
                    .SetSize(1)
                    .SetAbsoluteExpiration(TimeSpan.FromSeconds(absoluteExpirationTime));

                // Save data in cache.
                m_cache.Set(key, cacheEntry, cacheEntryOptions);
            }
            return cacheEntry; 
        }
    }
var logRepository = new CacheDecorator(new JsonRepository());
var logRepository = new CacheDecorator(new CsvRepository());

נשים לב לקלות שבה נוכל לעטוף כל טיפוס מהסוג של logRepository.

הסבר מפורט על Decorator

את הקוד המלא – ניתן למצוא כאן

האם אתה משפיע ?

הדבר שאנחנו צריכים לשאול את עצמינו : "האם אני משפיע בחברה שבה אני נמצא".

הרעיון הוא לשאוף להיות זה שמשפיע. לכל אדם יש את היכולת המיוחדת שלו, שרק הוא יכול להביא לצוות או לחברה שלו. שימו לב, כל יום יוצא שיר חדש, היינו יכולים לומר שכר שמענו את כל הלחנים והסיגנונות של המוזיקה בעולם, אבל שוב, כל חודש יוצא שיר חדש שנהפך ללהיט. למה? כי לכל אדם יש את הניגון המיוחד שלו, שרק הוא יודע לנגן!

נשאל את עצמנו כמה שאלות :

  1. האם ישנו תחום שהכנסתי משהו חדש?
  2. האם פיתחתי יכולות מסויימות?
  3. אולי חדשתי משהו? או הקלתי את צורת העבודה בדבר קטן וחדשני שהכנתי לצוות?
  4. האם כתבתי מדריכים, או מסמכים שיכולים לקדם את העבודה בצוות הפיתוח שלי? ( תחשבו, לא כל אחד בצוות בקי בכל הנושאים לעומק. אם נשתף את הידע בין כולנו, נקדם את העבודה בצורה מהירה ואיכותית יותר!)
  5. האם אני מדריך מישהו, מעביר את הידע?
  6. האם אני שואל את עצמי, במה אני יכול לעזור?

העתקת dll בזמן קומפילציה עם xcopy

באחד הפוסטים הקודמים היה לנו צורך ליצור אובייקטים בזמן ריצה, כך שכדי לקבל הפרדה מלאה של יחידות התוכנה, הסרנו את הרפרנסים לפרויקטים של המימושים הספציפיים, הדבר הזה מנע העתקה אוטומטית של ה dlls לאחר קומפילציה. דבר אשר גרם לנו חריגת זמן ריצה, היות ולא הצלחנו ליצור אובייקט בזמן ריצה כי ה dll של אותו פרויקט לא היה בתיקיית ההרצה!

הרעיון הוא ליצור פקודת העתקה ע"י הפקודה xcopy, מיד לאחר ש build מסתיים בהצלחה. נגדיר זאת בהגדרות של הפרויקט שאליו נרצה להעתיק.

חלונית ה build events
  1. נרצה להגדיר את האירועים שיתרחשו בזמן ה build
  2. נוכל להגדיר האם הפקודות ירוצו לאחר build מוצלח, או לא משנה מה הייתה התוצאה של ה build.
  3. נוכל להעזר בכלי שינגיש לנו קיצורי דרך לספריות שכנראה נרצה לגשת אליהם, לדוגמא הנתיב של הפרויקט או של כל ה solutiom.

כדי להעתיק קבצים נשתמש בפקודה xcopy, ניתן להעתיק תיקיות שלמות, קבצים, או לפלטר קבצים. אפשר לקרוא כאן על כמה אפשרויות מעניות.

הרחבה על פקודות המאקרו של VS אפשר לקרוא כאן על רבות מהן