part design_PET结构

part design_PET结构今天终于开始研究微软对于ASP.NET2.0的产品PetShop4.0了,这个产品从架构设计到编码,都有很多的想法值得去研究,而且此产品还引入了许多.net2.0的新特性。不过学习是个长期的过程,设计的思想不可能在段时间去领会,只能一个一个方面去学习和研究。今天研究了架构,遇到了不少问题,理解起来比较抽象,但还是有一点心得的。PetShop4.0采用了三层的架构,表现层、业务逻辑层和数据层。分

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用
今天终于开始研究微软对于ASP.NET2.0的产品PetShop4.0了,这个产品从架构设计到编码,都有很多的想法值得去研究 ,而且此产品还引入了许多.net2.0的新特性。不过学习是个长期的过程,设计的思想不可能在段时间去领会,只能一个一个方面去学习和研究。今天研究了 架构,遇到了不少问题,理解起来比较抽象,但还是有一点心得的。

PetShop4.0采用了三层的架构,表现层、业务逻辑层和数据层。

分层的优势:

1、使得各层相互独立,减少依赖性

2、方便开发人员职责分离,仅仅负责其中的某一块,而不用去考虑其它实现

3、方便管理和维护,其中一处的改动不会影响到其它的层

4、方便逻辑的复用

不足:

1、如果有新的功能加入到系统中,在自下而上的方法中,各个层都需要添加新的代码,小系统一般不会有太大的工作量,但是大系统往往比较麻烦

2、本来可以直接操作数据库完成对数据库的操作,但是由于分层,系统性能受到了一定的影响,对小的应用,还是使用不分层来实现对数据库的直接操作,可以取得较好的性能

3、分层之后,每层都有许多对应实现的模式,逻辑往往很抽象,给理解带来了困难,特别对于许多没有大型项目经验的人

整个系统的结构如下:

ps01.gif

系统架构简图

ps05.gif

详细的体系架构

表现层:系统的UI组件,直接面对使用者,主要负责最终用户跟系统的交互,包含基本的服务器端控件和跟页面有关的操作(js脚本)

业务逻辑层:是系统的核心层,所有的设计都是围绕该层进行设计,因为业务直接跟需求挂钩,比如用户的注册、登录等、用户订单的管理,宠物的管理等

数据访问层:进行数据存储并数据对象的持久化和各种操作(增、删、改、查),因为这层的设计比较重要,此层往往用工厂模式去实现支持不同的数据库,因而是理解架构的关键部分。

现从数据访问层入手,分析一下数据访问底层的设计:

ps06.gif

数据访问层的模块结构图

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层的是什么类型的对象呢?

在实现数据访问层的过程当中,使用了抽象工厂模式:
对于用户来说,工厂里产品如何生产的你不用知道,仅仅只去用工厂里生产出来的产品就可以了。

用工厂模式来实现了对SqlServerOracle数据库访问的操作,而BLL层不用知道也不用关心后台用的是哪一种数据库,它只要用接口就行了,接口中定义了要用的方法,当调用接口时会根据具体的情况再去调用底层数据访问操作。

DALFactory是关键,当BLL层要操作数据库时,DALFactory会根据具体情况再去使用SqlServerDALOracleDAL中的一个。这样系统上层只管调用,而下层实现细节。底下的数据层的实现细节对于上层来说被隐藏了。这样做到了分离的目标,尽量减少层与层之间的联系程度,保持各层的独立性。

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层的具 体实现的,即使底层有变动,也很少会影响到上一层的业务细节,因此,层与层之间得到了较好的分离。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/183601.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • acwing-371. 牧师约翰最忙碌的一天(2-SAT)「建议收藏」

    acwing-371. 牧师约翰最忙碌的一天(2-SAT)「建议收藏」牧师约翰在 9 月 1 日这天非常的忙碌。有 N 对情侣在这天准备结婚,每对情侣都预先计划好了婚礼举办的时间,其中第 i 对情侣的婚礼从时刻 Si 开始,到时刻 Ti 结束。婚礼有一个必须的仪式:站在牧师面前聆听上帝的祝福。这个仪式要么在婚礼开始时举行,要么在结束时举行。第 i 对情侣需要 Di 分钟完成这个仪式,即必须选择 Si∼Si+Di 或 Ti−Di∼Ti 两个时间段之一。牧师想知道他能否满足每场婚礼的要求,即给每对情侣安排Si∼Si+Di 或 Ti−Di∼Ti,使得这些仪式的时

    2022年8月9日
    3
  • jmeter进阶-webservice接口「建议收藏」

    jmeter进阶-webservice接口「建议收藏」常用的接口类型http、webservice(soap)、websocket、dabbo如何判断接口是否为webservice:(1)询问开发可知;(2)通过地址查看可知(结尾是wsdl);

    2022年7月4日
    24
  • 200行Html5+CSS3+JS代码实现动态圣诞树

    200行Html5+CSS3+JS代码实现动态圣诞树一、前言最近CSDN的热榜出现了很多用Python、C/C++等编程语言实现的圣诞树,这篇文章用前端三大杀手Html5、CSS、Js来实现动态圣诞树二、

    2022年7月25日
    13
  • 直连模式,pac模式和全局模式哪个好_全局代理模式

    直连模式,pac模式和全局模式哪个好_全局代理模式三种设置

    2022年10月19日
    2
  • 产品经理告诉你什么是PMF?什么是MVP?

    产品经理告诉你什么是PMF?什么是MVP?一、什么是PMF?PMF指的是产品与市场匹配的产品1.如何判断PMF的临界点?1、留存:30%的新用户次日留存2、新增用户DAU:大于1003、用户数:10万用户数DAU:单日活跃用户量,反应产品短期用户活跃度2.PMF几种类型1、更好体验的产品,如美图秀秀2、抓住细分市场,如uber、P2P3、全新的市场,如微博的@4、综合体,如Airbnb二、什么是MVP?MVP指的是对用户有价值的最小可用产品MVP打造阶段:对用户有价值的最小可用产品,从功能列表中定位出产品的核心功能,.

    2022年5月24日
    62
  • 圆柱体积公式怎么算立方米_长方体计算体积公式

    圆柱体积公式怎么算立方米_长方体计算体积公式想要求圆柱的体积必须要记住圆柱对应的公式,下面小编为大家提供圆柱体积怎么算,希望对大家有所帮助。求圆柱体积的算法求圆柱体积先要求圆基的半径。两个圆都会做,因为它们大小相同。如果你已经知道半径,你可以继续前进。如果你不知道半径,那么你可以用尺子测量圆的最宽部分,然后除以2。这将比测量直径的一半更准确。我们说,这个圆筒的半径是1英寸(2.5厘米)。把它写下来。如果你知道这个圆的直径,就把它分成2个。…

    2022年9月20日
    1

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号