- Mastering Windows Presentation Foundation
- Sheridan Yuen
- 940字
- 2021-06-24 16:49:05
Through interfaces
Going back to our auditing example, we could have declared these properties in an interface instead. Let's see what this might look like:
using System; namespace CompanyName.ApplicationName.DataModels.Interfaces { public interface IAuditable { DateTime CreatedOn { get; set; } User CreatedBy { get; set; } DateTime? UpdatedOn { get; set; } User UpdatedBy { get; set; } } }
Now, if a developer requires these properties, they can implement this interface as well as extending the Data Model base class:
Let's see an example of this in code now:
using System; using CompanyName.ApplicationName.DataModels.Interfaces; namespace CompanyName.ApplicationName.DataModels { public class Invoice : BaseDataModel, IAuditable { private DateTime createdOn; private DateTime? updatedOn; private User createdBy, updatedBy; public DateTime CreatedOn { get { return createdOn; } set { createdOn = value; NotifyPropertyChanged(); } } public User CreatedBy { get { return createdBy; } set { createdBy = value; NotifyPropertyChanged(); } } public DateTime? UpdatedOn { get { return updatedOn; } set { updatedOn = value; NotifyPropertyChanged(); } } public User UpdatedBy { get { return updatedBy; } set { updatedBy = value; NotifyPropertyChanged(); } } } }
Initially then, it seems as though this could be a better way to go, but let's continue to investigate the same scenario that we looked at with the base classes. Let's now imagine that we want to provide the same basic undo capability using interfaces. We didn't actually investigate which members would be required for this, but it will require both properties and methods.
This is where the interface approach starts to break down somewhat. We can ensure that implementers of our ISynchronization interface have particular properties and methods, but we have no control over their implementation of those methods. In order to provide the ability to undo changes, we need to provide the actual implementation of these methods, rather than just the required scaffolding.
If this was left up to the developers to implement each time they used the interface, they might not implement it correctly, or perhaps they might implement it differently in different classes and break the consistency of the application. Therefore, to implement some functionality, it seems as though we really do need to use some kind of base class.
However, we also have a third option that involves a mix of the two approaches. We could implement some functionality in a base class, but instead of deriving our Data Model classes from it, we could add a property of that type to them, so that they can still access its public members.
We could then declare an interface that simply has a single property of the type of this new base class. In this way, we would be free to add the different functionality from different base classes to just the classes that require them. Let's look at an example of this:
public interface IAuditable { Auditable Auditable { get; set; } }
This Auditable class would have the same properties as those in the previous IAuditable interface shown in the preceding code. The new IAuditable interface would be implemented by the Data Model classes by simply declaring a property of type Auditable :
public class User : IAuditable { private Auditable auditable; public Auditable Auditable { get { return auditable; } set { auditable = value; } } ... }
It could be used by the framework, for example, to output the names of each user and when they were created into a report. In the following example, we use the Interpolated Strings syntax that was introduced in C# 6.0 for constructing our string. It's like the string.Format method, but with the method call replaced with a $ sign and the numerical format items replaced with their related values:
foreach (IAuditable user in Users) { Report.AddLine($"Created on {user.Auditable.CreatedOn}" by
{user.Auditable.CreatedBy.Name});
}
Most interestingly, as this interface could be implemented by many different types of object, the preceding code could also be used with objects of different types. Note this slight difference:
List<IAuditable> auditableObjects = GetAuditableObjects(); foreach (IAuditable user in auditableObjects) { Report.AddLine($"Created on {user.Auditable.CreatedOn}" by
{user.Auditable.CreatedBy.Name}); }
It's worth pointing out this useful ability to work with objects of different types is not limited to interfaces. This can also be achieved just as easily with base classes. Imagine a View that enabled the end user to edit a number of different types of object.
If we added a property named PropertyChanges, that returned details of changed properties, into the BaseSynchronizableDataModel class that we will see later, in the Constructing a custom application framework section, we could use this very similar code to display a confirmation of the changes from each object back to the user:
List<BaseSynchronizableDataModel> baseDataModels = GetBaseDataModels(); foreach (BaseSynchronizableDataModel baseDataModel in baseDataModels) { if (baseDataModel.HasChanges) FeedbackManager.Add(baseDataModel.PropertyChanges); }
We have a number of choices when it comes to encapsulating pieces of pre-packaged functionality into our Data Model classes. Each of these methods that we have investigated so far have strengths and weaknesses. If we're sure that we want some pre-written functionality in every one of our Data Model classes, like that of the INotifyPropertyChanged interface, then we can simply encapsulate it in a base class and derive all of our Model classes from that.
If we just want our Models to have certain properties or methods that can be called from other parts of the framework, but are not concerned with the implementation, then we can use interfaces. If we want some combination of the two ideas, then we can implement a solution using the two methods together. It is up to us to choose the solution that best fits the requirements in hand.
- 演進(jìn)式架構(gòu)(原書第2版)
- 編程卓越之道(卷3):軟件工程化
- C/C++算法從菜鳥到達(dá)人
- Python網(wǎng)絡(luò)爬蟲從入門到實(shí)踐(第2版)
- Practical DevOps
- oreilly精品圖書:軟件開發(fā)者路線圖叢書(共8冊)
- Monitoring Elasticsearch
- 深入RabbitMQ
- Visual FoxPro程序設(shè)計(jì)
- Visual Basic程序設(shè)計(jì)上機(jī)實(shí)驗(yàn)教程
- IBM Cognos Business Intelligence 10.1 Dashboarding cookbook
- 現(xiàn)代C++編程實(shí)戰(zhàn):132個(gè)核心技巧示例(原書第2版)
- 計(jì)算機(jī)應(yīng)用基礎(chǔ)項(xiàng)目化教程
- Mastering Apache Storm
- jQuery for Designers Beginner's Guide Second Edition