248. Implementing a fluent syntax part 2

אז פעם קודמת ראינו איך אפשר להתחיל לממש Fluent syntax.

אחת הבעיות שיש עם המימוש הזה, הוא שהSyntax הוא חסר זכרון.

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

1
2
3
4
5
6
7
8
dataSet.
AddTable("DEPARTMENT").
WithColumn<int>("PK").
MakePrimaryKey().
WithColumn<string>("NAME").
WithDefaultValue("John Doe").
MakePrimaryKey();
// Compiles

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

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface ITableDefinitionSyntax
{
ITableOrColumnDefinitionSyntax<T> WithColumn<T>(string name);
}
public interface IColumnDefaultValueDefinitionSyntax<T>
{
ITableOrColumnDefinitionSyntax<T> WithDefaultValue(T value);
}
public interface ITableOrColumnDefinitionSyntax<T> : ITableDefinitionSyntax, IColumnDefaultValueDefinitionSyntax<T>
{
}

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

באשר לPrimary Key: נוסיף ממשקים מתאימים:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface IColumnMakePrimaryKeyDefinitionSyntax<T>
{
ITableOrColumnDefinitionSyntax<T> MakePrimaryKey();
}
public interface IColumnDefaultValueMakePrimaryKeyDefinitionSyntax<T> : IColumnMakePrimaryKeyDefinitionSyntax<T>
{
ITableOrColumnMakePrimaryKeyDefinitionSyntax<T> WithDefaultValue(T value);
}
public interface ITableWithPrimaryKeyDefinitionSyntax
{
ITableOrColumnMakePrimaryKeyDefinitionSyntax<T> WithColumn<T>(string name);
}
public interface ITableOrColumnMakePrimaryKeyDefinitionSyntax<T> : IColumnDefaultValueMakePrimaryKeyDefinitionSyntax<T>, ITableWithPrimaryKeyDefinitionSyntax
{
}

יש פה קצת רקורסיה, אבל נראה לי שנסתדר.

בהנחה שיש לנו Extension Method מתאים:

1
2
3
4
public static ITableWithPrimaryKeyDefinitionSyntax AddTable(this DataSet dataSet, string name)
{
// ...
}

זה מאפשר לנו לכתוב את הקוד הבא:

1
2
3
4
5
6
7
dataSet.
AddTable("DEPARTMENT").
WithColumn<int>("PK").
WithDefaultValue(0).
MakePrimaryKey().
WithColumn<string>("NAME").
WithDefaultValue("John Doe");

אבל ברגע שהפעלנו את הפונקציה MakePrimaryKey, לא נוכל להפעילה יותר!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
dataSet.
AddTable("DEPARTMENT").
WithColumn<int>("PK").
WithDefaultValue(0).
MakePrimaryKey().
WithColumn<string>("NAME").
WithDefaultValue("John Doe").
MakePrimaryKey(); // Won't compile
dataSet.
AddTable("DEPARTMENT").
WithColumn<int>("PK").
WithDefaultValue(0).
MakePrimaryKey().
MakePrimaryKey(). // Won't compile
WithColumn<string>("NAME").
WithDefaultValue("John Doe");
dataSet.
AddTable("DEPARTMENT").
WithColumn<int>("PK").
WithDefaultValue(0).
MakePrimaryKey().
WithColumn<string>("NAME").
MakePrimaryKey(). // Won't compile
WithDefaultValue("John Doe");

מגניב ביותר! 😃

החיסרון הוא, כמובן, שצריך ליצור יותר Interfaceים ולהרכיב אותם בצורה הנכונה. אבל אם אנחנו רוצים למנוע שימוש יותר מפעם אחת במשהו, אז זה בהחלט שווה את ההשקעה. (לדוגמה אם חשוב לנו שלא יהיה אפשר להגדיר את Default Value פעמיים, נוכל לטפל בזה ע"י הוספת Interfaceים מתאימים ש"זוכרים" גם את הסטאטוס של MakePrimaryKey וגם את הסטאטוס של WithDefaultValue)

המשך יום צף טוב!

שתף