大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE稳定放心使用
今天终于开始研究微软对于ASP.NET2.0的产品PetShop4.0了,这个产品从架构设计到编码,都有很多的想法值得去研究 ,而且此产品还引入了许多.net2.0的新特性。不过学习是个长期的过程,设计的思想不可能在段时间去领会,只能一个一个方面去学习和研究。今天研究了 架构,遇到了不少问题,理解起来比较抽象,但还是有一点心得的。
PetShop4.0采用了三层的架构,表现层、业务逻辑层和数据层。
分层的优势:
1、使得各层相互独立,减少依赖性
2、方便开发人员职责分离,仅仅负责其中的某一块,而不用去考虑其它实现
3、方便管理和维护,其中一处的改动不会影响到其它的层
4、方便逻辑的复用
不足:
1、如果有新的功能加入到系统中,在自下而上的方法中,各个层都需要添加新的代码,小系统一般不会有太大的工作量,但是大系统往往比较麻烦
2、本来可以直接操作数据库完成对数据库的操作,但是由于分层,系统性能受到了一定的影响,对小的应用,还是使用不分层来实现对数据库的直接操作,可以取得较好的性能
3、分层之后,每层都有许多对应实现的模式,逻辑往往很抽象,给理解带来了困难,特别对于许多没有大型项目经验的人
整个系统的结构如下:
系统架构简图
详细的体系架构
业务逻辑层:是系统的核心层,所有的设计都是围绕该层进行设计,因为业务直接跟需求挂钩,比如用户的注册、登录等、用户订单的管理,宠物的管理等
数据访问层:进行数据存储并数据对象的持久化和各种操作(增、删、改、查),因为这层的设计比较重要,此层往往用工厂模式去实现支持不同的数据库,因而是理解架构的关键部分。
现从数据访问层入手,分析一下数据访问底层的设计:
IDAL:抽象出数据访问的方法,要引用Model里面的实体对象
Model:包含实体对象
DBUtility:封装访问数据库的方法,针对不同的数据库有不同的实现(如SQLHelper.cs和OracleHelper.cs),是数据访问辅助工具
SQLHelper:封装ADO.NET的相关操作,用来优化操作
SQLServerDAL :以SQLServer的方式实现接口
OracleDAL:以Oracle的方式实现的接口
以ProductInfo为例,以SQLServer作为数据库例子:
ProductInfo类对产品进行操作(增、删、改、查),这些操作是通过DBUtility中的SQLHelper类来实现对数据库的操作的。
例如:
public static readonly string ConnectionStringLocalTransaction = ConfigurationManager.ConnectionStrings[“SQLConnString1”].ConnectionString;
public static readonly string ConnectionStringInventoryDistributedTransaction = ConfigurationManager.ConnectionStrings[“SQLConnString2”].ConnectionString;
完成在配置文件中读取相应的数据库连接字符串
然后再PetShop.SQLServerDAL 命名空间下的Product类中通过调用SQLHelper类中的方法完成对数据库的操作
例如:
private const string SQL_SELECT_PRODUCTS_BY_CATEGORY = “SELECT Product.ProductId, Product.Name, Product.Descn, Product.Image, Product.CategoryId FROM Product WHERE Product.CategoryId = @Category”;
private const string SQL_SELECT_PRODUCTS_BY_SEARCH1 = “SELECT ProductId, Name, Descn, Product.Image, Product.CategoryId FROM Product WHERE ((“;
设置SQL命令,然后进行调用:
public ProductInfo GetProduct(string productId) {
ProductInfo product = null;
SqlParameter parm = new SqlParameter(PARM_PRODUCTID, SqlDbType.VarChar, 10);
parm.Value = productId;
using (SqlDataReader rdr =
SqlHelper.ExecuteReader(
SqlHelper.ConnectionStringLocalTransaction, CommandType.Text, SQL_SELECT_PRODUCT, parm))
if (rdr.Read())
product = new ProductInfo(rdr.GetString(0), rdr.GetString(1), rdr.GetString(2), rdr.GetString(3), rdr.GetString(4));
else
product = new ProductInfo();
return product;
}
以上使用了
SqlHelper.ExecuteReader()方法通过操作ADO.NET实现对数据库记录的更新.
程序片段:
using PetShop.IDAL;
public class Product : IProduct{ }
说明Product 实现了在命名空间PetShop.IDAL中的IProduct接口,因此最后对数据的操作最终要返回一个Product对象,然后去调用该对象实现的方法.
PetShop.IDAL:数据访问层的接口
在此命名空间当中,定义了各种实体对象应该实现的接口,以下是定义在该命名空间中的IProduct接口。
using System;
using System.Collections.Generic;
using PetShop.Model;
namespace PetShop.IDAL{
/// <summary>
/// Interface for the Product DAL
/// </summary>
public interface IProduct{
/// <summary>
/// Method to search products by category name
/// </summary>
/// <param name=”category”>Name of the category to search by</param>
/// <returns>Interface to Model Collection Generic of search results</returns>
IList<ProductInfo> GetProductsByCategory(string category);
/// <summary>
/// Method to search products by a set of keyword
/// </summary>
/// <param name=”keywords”>An array of keywords to search by</param>
/// <returns>Interface to Model Collection Generic of search results</returns>
IList<ProductInfo> GetProductsBySearch(string[] keywords);
/// <summary>
/// Query for a product
/// </summary>
/// <param name=”productId”>Product Id</param>
/// <returns>Interface to Model ProductInfo for requested product</returns>
ProductInfo GetProduct(string productId);
}
}
作为上层的BLL层,只管去调用这个接口,但是不管接口是怎么实现的,但是当BLL层调用的时候,通过的这个接口层最终返回给BLL层的是什么类型的对象呢?
在实现数据访问层的过程当中,使用了抽象工厂模式:
对于用户来说,工厂里产品如何生产的你不用知道,仅仅只去用工厂里生产出来的产品就可以了。
用工厂模式来实现了对SqlServer和Oracle数据库访问的操作,而BLL层不用知道也不用关心后台用的是哪一种数据库,它只要用接口就行了,接口中定义了要用的方法,当调用接口时会根据具体的情况再去调用底层数据访问操作。
DALFactory是关键,当BLL层要操作数据库时,DALFactory会根据具体情况再去使用SqlServerDAL和OracleDAL中的一个。这样系统上层只管调用,而下层实现细节。底下的数据层的实现细节对于上层来说被隐藏了。这样做到了分离的目标,尽量减少层与层之间的联系程度,保持各层的独立性。
DALFactory工厂模式是如何决定应该用SqlServerDAL还是用OracleDAL的呢?
以下是
DALFactory关于
IProduct接口
的实现代码:
using System.Reflection;
using System.Configuration;
namespace PetShop.DALFactory {
/// <summary>
/// This class is implemented following the Abstract Factory pattern to create the DAL implementation
/// specified from the configuration file
/// </summary>
public sealed class DataAccess {
// Look up the DAL implementation we should be using
private static readonly string path = ConfigurationManager.AppSettings[“WebDAL”];
//Return interface of
IProduct
public static PetShop.IDAL.IProduct CreateProduct() {
string className = path + “.Product”;
return (PetShop.IDAL.IProduct)Assembly.Load(path).CreateInstance(className);
}
}
}
在web.config里面读取的配置文件(该配置文件设置了用户使用的数据库的类型):
<appSettings>
<!– Pet Shop DAL configuration settings. Possible values: PetShop.SQLServerDAL for SqlServer, PetShop.OracleServerDALfor Oracle. –>
<add key=”WebDAL” value=”PetShop.SQLServerDAL”/>
<add key=”OrdersDAL” value=”PetShop.SQLServerDAL”/>
<add key=”ProfileDAL” value=”PetShop.SQLProfileDAL”/>
<appSettings>
在上面的程序片段中,
private static readonly string path = ConfigurationManager.AppSettings[“WebDAL”];
该句用来在配置文件中读取
AppSettings
节点的值,该配置文件说明了系统应该使用的是那一个数据库(SQLServer或者Oracle或者其它),读取的过程是通过读取key,然后的到该key所对应的value。
该句:
public static PetShop.IDAL.IProduct CreateProduct() {
string className = path + “.Product”;
return (PetShop.IDAL.IProduct)Assembly.Load(path).CreateInstance(className);
}
是问题的关键之处,通过该方法最终返回了
IProduct接口类型。
但是
该句 string className = path + “.Product”; 返回了
PetShop.SQLServerDAL.Product对象
然后使用:
Assembly.Load(
PetShop.SQLServerDAL
).CreateInstance(
PetShop.SQLServerDAL.Product
);利用反射特性动态加载
PetShop.SQLServerDAL.dll,同时创建了
PetShop.SQLServerDAL.Product
对象的实例,最终
以接口PetShop.IDAL.
IProduct
类型返回。
当BLL层调用数据访问层的时候,通过IDAL接口使用了
类PetShop.SQLServerDAL.Product,进而去使用了该类的代码,本质还是调用了对象的实例,不像简单的New一个对象的实例,如果使用
Product aProduct=new Product(); 则表示没有完全将对象完全分离,对象和对象的实例化有着太强的联系。而使用返回接口的方法,很好的将实例化和对象之间的关系弱化,达到了较好的分离。
然后看一下BLL层是如何调用IDAL层的:
BLL层的代码:
using System.Collections.Generic;
using PetShop.Model;
using PetShop.IDAL;
namespace PetShop.BLL {
/// <summary>
/// A business component to manage products
/// </summary>
public class Product {
// Get an instance of the Product DAL using the DALFactory
// Making this static will cache the DAL instance after the initial load
private static readonly IProduct dal = PetShop.DALFactory.DataAccess.CreateProduct();
/// <summary>
/// Query for a product
/// </summary>
/// <param name=”productId”>Product Id</param>
/// <returns>ProductInfo object for requested product</returns>
public ProductInfo GetProduct(string productId) {
// Return empty product if the string is empty
if(string.IsNullOrEmpty(productId))
return new ProductInfo();
// Get the product from the data store
return dal.GetProduct(productId);
}
}
}
使用该句:
private static readonly IProduct dal = PetShop.DALFactory.DataAccess.CreateProduct();
使用工厂得到
Product DAL的一个实例化的对象,然后通过该对象去调用相应的方法,如下:
dal.GetProduct(productId);
这样,BLL层就可以直接调用DAL层的接口完成对数据库的操作,但是BLL层并不知道它操作的数据库是那个数据库,而这些都是由DAL Factory去实现的,因此BLL层只管去调用接口,而对底层访问数据库的实现细节一概不知,如果BLL层有较大的变动,基本是不会影响到DAL层的具 体实现的,即使底层有变动,也很少会影响到上一层的业务细节,因此,层与层之间得到了较好的分离。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/183601.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...