到目前为止,我们一直在研究如何在C#中创建一个类,然后向其添加属性和方法的具体细节。现在我们想给应用程序添加额外的类。这是一个客户关系管理类型的应用程序,所以我们需要像订单类、产品类、OrderItem类,以及其他一些类。在本教程中,我们将开始构建这些类,以便与我们现有的Customer类一起工作,同时注意耦合、凝聚、关注点分离和设计模式等要素。
首先,让我们定义一下耦合、凝聚、关注点分离和设计模式。
- 耦合是指类之间相互依赖的程度。
- 凝聚力是指类的成员与类的目的相关的程度。
- 关注点分离(Separation of Concerns)告诉我们要将一个应用程序分解成重叠度最小的部分。每个部分都负责一个单独的关注点。
- 设计模式是定义适当的类和它们的相关关系的通用做法。
产品类
我们的产品类的代码如下所示,首先我们可以看到,它有一个构造函数,接受一个整数id,代表 productId.它还有几个属性,我们可以用它们来表示一个产品。一个产品会有哪些东西呢?它有一些东西,比如a CurrentPrice, a ProductId, a ProductDescription,和a ProductName.此外,有一个 Validate()的方法,它验证了 ProductName的完整性,以及 CurrentPrice.我们也有一个 Retrieve()和 Save()方法。这个类的文件名是Product.cs。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CRMBIZ
{
public class Product
{
public Product()
{
}
public Product(int productId)
{
this.ProductId = productId;
}
public Decimal? CurrentPrice { get; set; }
public int ProductId { get; private set; }
public string ProductDescription { get; set; }
public string ProductName { get; set; }
public Product Retrieve(int productId)
{
return new Product();
}
public bool Save()
{
return true;
}
public bool Validate()
{
var isValid = true;
if (string.IsNullOrWhiteSpace(ProductName)) isValid = false;
if (CurrentPrice == null) isValid = false;
return isValid;
}
}
}
订单类
订单类也使用了一个默认的构造函数,以及一个辅助构造函数,该构造函数接受一个整数代表 orderId.订单类有两个属性 OrderDate和 OrderId.我们也可以看到这些相同的方法 Retrieve(), Save()和 Validate().订单类的文件名是Order.cs。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CRMBIZ
{
public class Order
{
public Order()
{
}
public Order(int orderId)
{
this.OrderId = orderId;
}
public DateTimeOffset? OrderDate { get; set; }
public int OrderId { get; private set; }
public Order Retrieve(int orderId)
{
return new Order();
}
public bool Save()
{
return true;
}
public bool Validate()
{
var isValid = true;
if (OrderDate == null) isValid = false;
return isValid;
}
}
}
OrderItem类
我们的CRM应用大纲中的最后一个类将是OrderItem类,它被列在下面。一切看起来都很好,但我们确实看到,所有这些类都有许多方法。我们可能要对这些方法进行一些重构。按照惯例,OrderItem类的文件名是OrderItem.cs。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CRMBIZ
{
public class OrderItem
{
public OrderItem()
{
}
public OrderItem(int orderItemId)
{
this.OrderItemId = orderItemId;
}
public int OrderItemId { get; private set; }
public int OrderQuantity { get; set; }
public int ProductId { get; set; }
public decimal? PurchasePrice { get; set; }
public OrderItem Retrieve(int orderItemId)
{
return new OrderItem();
}
public bool Save()
{
return true;
}
public bool Validate()
{
var isValid = true;
if (OrderQuantity <= 0) isValid = false;
if (ProductId <= 0) isValid = false;
if (PurchasePrice == null) isValid = false;
return isValid;
}
}
}
重构类的责任
到目前为止,我们有一个客户、产品、订单和OrderItem类。这是一个很好的开始,可以代表一个基本的CRM应用。然而,有几个问题。所有这些类都有一个 Retrieve()和 Save()方法,可以提取到一个存储库类中。此外,我们应该从客户类中移除一些责任,并将其放在地址类中。让我们现在开始解决这些问题。
地址类
下面是地址类的代码,可以在Address.cs中找到。它被用来表示一个地址,其属性为 AddressId, AddressType, StreetLine1, StreetLine2, City, State, PostalCode、 、 和 Country.刚好可以完成它的工作。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CRMBIZ
{
public class Address
{
public Address()
{
}
public Address(int addressId)
{
this.AddressId = addressId;
}
public int AddressId { get; private set; }
public int AddressType { get; set; }
public string StreetLine1 { get; set; }
public string StreetLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
}
}
为数据持久性创建存储库类
上面的原始类可以从删除 Retrieve()和 Save()方法可以从中受益。存储库类可以处理与数据库的交互。这个逻辑不需要像上面那样在每一个类中重新创建。最好是将这些逻辑提取出来,放在自己的资源库中。使用资源库模式是面向对象编程中一个非常流行的惯例,在C#、Java、PHP等中都有。
CustomerRepository类
在这个类中,我们需要做的是将客户类中的Retrieve()和Save()方法剪切并粘贴到这个存储库类中。这样一来,与数据库交互的工作就被放在自己的类中,以便更好地分离问题。它的文件名是CustomerRepository.cs。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CRMBIZ
{
public class CustomerRepository
{
public Customer Retrieve(int customerId)
{
Customer customer = new Customer(customerId);
if (customerId == 1)
{
customer.EmailAddress = "swilliams@greenenergy.com";
customer.FirstName = "Susan";
customer.LastName = "Williams";
}
return customer;
}
public List<Customer> Retrieve()
{
return new List<Customer>();
}
public bool Save()
{
return true;
}
}
}
OrderRepository类
我们还可以创建一个OrderRepository类来处理与数据存储的交互,把这个功能从基类Order中移出来。因此,再次将Retrieve()和Save()的代码从Order类中移出,现在将它们放在OrderRepository类中。请注意,到目前为止,在所有这些存储库类的例子中,我们只是用硬编码来表示一个订单或一个产品等。与数据库交互的实际代码可以在以后添加。现在我们这样设置,我们甚至可以创建一些自动测试来确保代码的工作。下面是OrderRepository.cs。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CRMBIZ
{
public class OrderRepository
{
public Order Retrieve(int orderId)
{
Order order = new Order(orderId);
if (orderId == 10)
{
order.OrderDate = new DateTimeOffset(2019, 4, 14, 10, 00, 00, new TimeSpan(7, 0, 0));
}
return order;
}
public bool Save()
{
return true;
}
}
}
产品资源库类
这个类被设置为public,我们再次将Retrieve()和Save()方法从Product类中移出,放到ProductRepository类中。这些方法再次使用一些简单的硬编码数据来模拟与数据库的工作。这里是ProductRepository.cs文件。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CRMBIZ
{
public class ProductRepository
{
public Product Retrieve(int productId)
{
Product product = new Product(productId);
if (productId == 2)
{
product.ProductName = "Lenovo Laptop";
product.ProductDescription = "Carbon X1";
product.CurrentPrice = 1599.99M;
}
return product;
}
public bool Save()
{
return true;
}
}
}
AddressRepository类
我们可以创建的最后一个资源库类是AddressRepository.cs类。这个类比较复杂,因为它可以检索一个客户的单个地址或地址列表。RetrieveByCustomerId()方法返回一个IEnumerable。IEnumerable是返回数据序列的推荐方式,因为其结果为方法的调用者提供了更多的灵活性。这个方法需要一个参数,客户ID是一个整数值。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CRMBIZ
{
public class AddressRepository
{
public Address Retrieve(int addressId)
{
Address address = new Address(addressId);
if (addressId == 1)
{
address.AddressType = 1;
address.StreetLine1 = "Microsoft Way";
address.StreetLine2 = "C# Blvd";
address.City = "Seattle";
address.State = "Washington";
address.Country = "United States";
address.PostalCode = "98052";
}
return address;
}
public IEnumerable<Address> RetrieveByCustomerId(int customerId)
{
var addressList = new List<Address>();
Address address = new Address(1)
{
AddressType = 1,
StreetLine1 = "Microsoft Way",
StreetLine2 = "C# Blvd",
City = "Seattle",
State = "Washington",
Country = "United States",
PostalCode = "98052"
};
addressList.Add(address);
address = new Address(2)
{
AddressType = 2,
StreetLine1 = "Happy Island",
City = "Saltwater Shores",
State = "Washington",
Country = "United States",
PostalCode = "98569"
};
addressList.Add(address);
return addressList;
}
public bool Save(Address address)
{
return true;
}
}
}
测试 CustomerRepository 类
我们可以使用类似于这里的代码来测试存储库类。这段代码测试了 CustomerRepository 类,你可以创建类似的类来测试 OrderRepository 和 ProductRepository 类。
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CRMBIZ;
namespace CRMBIZ.Test
{
[TestClass]
public class CustomerRepositoryTest
{
[TestMethod]
public void RetrieveExisting()
{
var customerRepository = new CustomerRepository();
var expected = new Customer(1)
{
EmailAddress = "swilliams@greenenergy.com",
FirstName = "Susan",
LastName = "Williams"
};
var actual = customerRepository.Retrieve(1);
Assert.AreEqual(expected.CustomerId, actual.CustomerId);
Assert.AreEqual(expected.EmailAddress, actual.EmailAddress);
Assert.AreEqual(expected.FirstName, actual.FirstName);
Assert.AreEqual(expected.LastName, actual.LastName);
}
}
}
运行测试是给我们一个大拇指!

CRM应用程序的C#类总结
在本教程中,我们创建了一些类,以便与C#中的CRM类型的应用程序一起工作。一旦创建了这些类,我们就对每个类进行评估,看看我们是否可以简化以减少责任。我们发现我们可以,并将一些逻辑提取到相关的资源库类。采取这种方法可以最大限度地减少耦合,最大限度地提高内聚力,简化维护,并有助于使代码更易测试。我们很快就会看到这些类是如何一起工作的,当我们谈论类之间的关系时。