官术网_书友最值得收藏!

Providing services

The job of the base classes and interfaces in our application framework are to encapsulate functionality that is commonly used by our View Models and Data Models. When the required functionality is more complex, or when it involves particular resources, or external connections, we implement it in separate service, or manager classes. For the remainder of this book, we will refer to these as manager classes. In larger applications, these are typically provided in a separate project.

Encapsulating them in a separate project enables us to reuse the functionality from these classes in our other applications. Which classes we use in this project will depend on the requirements of the application that we're building, but it will often include classes that provide the ability to send emails, to access the end user's hard drive, to export data in various formats, or to manage global application state for example.

We will investigate a number of these classes in this book, so that we have a good idea of how to implement our own custom manager classes. The most commonly used of these classes can normally be accessed directly from the base View Model class via properties. There are a few different ways that we can expose these classes to the View Models, so let's examine them.

When a manager class is used often, and for short durations each time, we can expose a new instance of them each time, as follows:

public FeedbackManager FeedbackManager 
{ 
  get { return new FeedbackManager(); }  
} 

However, if a manager class is required for the life of the application because it must remember a particular state or configuration, for example, then we typically use the static keyword in one way or another. The simplest option would be to declare a normal class, but expose it via a static property:

private static StateManager stateManager = new StateManager(); 
   
...
    
public static StateManager StateManager 
{ 
  get { return stateManager; }  
} 

An alternative method of having one and only one instance of a class being instantiated and having it stay alive for as long as the application is running is for us to use the Singleton pattern. While it was all the rage twenty or so years ago, it has unfortunately recently fallen foul of more modern programming principles, such as the likes of SOLID, which states that each class should have a single responsibility.

The Singleton pattern breaks this principle as it serves whatever purpose we design it for, but it is also responsible for instantiating itself and maintaining a single access point. Before discussing the merits and pitfalls of this pattern further, let's take a look at how we might implement it in our manager class:

namespace CompanyName.ApplicationName.Managers 
{ 
  public class StateManager 
  { 
    private static StateManager instance; 
 
    private StateManager() { } 
 
    public static StateManager Instance 
    { 
      get { return instance ?? (instance = new StateManager()); } 
    } 
    
    ...
  } 
} 

Note that it can be implemented in a variety of ways, but this particular way uses lazy initialization, where the instance is not instantiated until it is first referenced via the Instance property. Using the ?? operator again, the Instance property getter can be read as "return the one and only instantiated instance if it is not null, or, if it is, instantiate the one and only instance and then return it." The significant part of this pattern is that as there is no public constructor and, therefore, the class cannot be externally instantiated, this property is the sinlgle way to access the internal object.

However, this is the very part that causes trouble for some developers, as this makes inheritance impossible with these classes. In our case though, we won't need to extend our StateManager class, so that is not a concern for us. Others may point to the problem that exposing this Singleton class, as shown in the following code, will tightly couple it to the base View Model class that it is declared in:

public StateManager StateManager 
{ 
  get { return StateManager.Instance; }  
} 

While this is true, what harm is that with this class? Its purpose is to maintain the state of user settings, common or default values, and values for UI display and operation statuses. It contains no resources and no real reason to avoid using it when running unit tests, so in this case, the tight coupling is inconsequential. In this regard, the Singleton pattern continues to be a useful tool in the right situations, but we should certainly be aware of its pitfalls all the same.

However, if a particular manger class does utilize resources or creates some form of connection with the outside world, for example, like an EmailManager would, then we will need to create an interface for it to maintain our Separation of Concerns. Remember that interfaces enable us to disconnect the actual application components and replace them with mock components while testing. In these cases, we have to expose the functionality in the base classes slightly differently:

private IEmailManager emailManager; 
 
...
     
public BaseViewModel(IEmailManager emailManager) 
{ 
  this.emailManager = emailManager; }  
} 
    
...
    
public IEmailManager EmailManager 
{ 
  get { return emailManager; }  
} 

The general idea here is for us to have no direct contact with the manager class in hand, instead accessing its functionality through the interface methods and properties. By doing this, we are able to decouple the manager class from the View Models that use it and therefore enable them to be used independently of each other. Note that this is a very simple example of Dependency Injection.

主站蜘蛛池模板: 沂南县| 公主岭市| 精河县| 漳平市| 新津县| 石渠县| 远安县| 汤阴县| 永仁县| 临高县| 贵南县| 万盛区| 明水县| 马尔康县| 湘潭市| 巴马| 崇文区| 云龙县| 宝应县| 怀仁县| 万安县| 谢通门县| 茌平县| 青州市| 甘洛县| 吴堡县| 克拉玛依市| 武定县| 前郭尔| 苗栗市| 徐水县| 依安县| 宁海县| 鸡东县| 手游| 神池县| 始兴县| 舟山市| 江永县| 黄大仙区| 青田县|