.Net平台-MVP模式初探(一)

MVP架构模式详解 MVP(Model-View-Presenter)架构通过将应用程序分为三个主要组件,实现了关注点分离,提高了代码的可维护性、可测试性和可读性。它特别适用于需要清晰结构和高测试覆盖率的中大型应用程序。虽然MVP架构存在一些挑战,如代码量增加、学习曲线和潜在的Presenter膨胀问题,但通过遵循最佳实践和合理规划,这些问题可以得到有效缓解。随着技术的发展,MVP也在不断演进,与新的技术和模式结合,继续发挥其价值。 阅读详情


为什么要写这篇文章

      笔者当前正在负责研究所中一个项目,这个项目基于.NET平台,初步拟采用C/S部署体系,所以选择了Windows Forms作为其UI。经过几此迭代,我们发现了一个问题:虽然业务逻辑已经封装到Services层中,但诸多的UI逻辑仍然弥漫在各个事件Listener中,使得UI显得臃肿不堪,并且存在诸多重复性代码。另外,需求提供方说,根据实际需要,不排除将部署结构改为B/S的可能性,甚至可能会要求此系统同时支持C/S和B/S两种部署方式。那么,如果保持目前将UI逻辑编码到Windows Forms中的方式,到时这些UI逻辑将无法复用,修改部署方式的代价很大。

      为了解决以上两个问题,笔者和相关人员商量后,决定引入既有成熟模式,重新设计表示层的架构方式,并重构既有代码。

      提到表示层(Presentation Layer)的模式,我想大家脑海中第一个闪过的很可能是经典的MVC(Model-View-Controller)。我最初也准备使用MVC,但经过分析和实验后,我发现MVC并不适合目前的情况,因为MVC的结构相对复杂,Model和View之间要实现一个Observer模式,并实现双向通信。这样重构起来Services层也必须修改。我并不想修改Services层,而且我想将View和Model彻底隔离,因为我个人并不喜欢View和Model直接通信的架构方式。最终,我选择了MVP(Model-View-Presenter)模式。

      经过两天的重构和验证,目前已经将MVP正式引入项目的表示层,并且解决了上文提到的两个问题。在这期间,积累了少许关于在.NET平台上实践MVP的经验,在这里汇集成此文,和朋友们共享。

UI与P Logic

      首先,我想先明确一下UI和P Logic的概念。

      表示层可以拆分为两个部分:User Interface(简称UI)和Presentation Logic(简称P Logic)。

      UI是系统与用户交互的界面性概念,它的职责有两个——接受用户的输入和向用户展示输出。UI应该是一个纯静态的概念,本身不应包含任何逻辑,而单纯是一个接受输入和展示输出的“外壳”。例如,一个不包含逻辑的Windows Form,一张不包含逻辑的页面,一个不包含逻辑的Flex界面,都属于UI。

      P Logic是表示层应有的逻辑性内容。例如,某个文本内容不能为空,当某个事件发生时获取界面上哪些内容,这都属于P Logic。应该指出,P Logic应该是抽象于具体UI的,它的本质是逻辑,可以复用到任何与此逻辑相符的UI。

      UI与P Logic之间的联系是事件,UI可以根据用户的动作触发各种事件,P Logic响应事件并执行相应的逻辑。P Logic对UI存在约束作用,P Logic规定一套UI契约,UI要根据契约实现,才能被相应的P Logic调用。

      下图展示了UI与P Logic的结构及交互原理。

图1、UI与P Logic

Model-View-Presenter模式

      MVP模式最早由Taligent的Mike Potel在《MVP: Model-View-Presenter The Taligent Programming Model for C++ and Java》(点击这里下载)一文中提出。MVP的提出主要是为了解决MVC模式中结构过于复杂和模型-视图耦合性过高的问题。MVP的核心思想是将UI分离成View,将P Logic分离成Presenter,而业务逻辑和领域相关逻辑都分离到Model中。View和Model完全解除耦合,不再像MVC中实现一个Observer模式,两者的通信则依靠Presenter进行。Presenter响应View接获的用户动作,并调用Model中的业务逻辑,最后将用户需要的信息返回给View。

      下图直观表示了MVP模式:

图2、MVP模式

      图2清楚地展示了MVP模式的几个特点:

      1、View和Model完全解耦,两者不发生直接关联,通过Presenter进行通信。

      2、Presenter并不是与具体的View耦合,而是和一个抽象的View Interface耦合,View Interface相当于一个契约,抽象出了对应View应实现的方法。只要实现了这个接口,任何View都可以与指定Presenter兼容,从而实现了P Logic的复用性和视图的无缝替换。

      3、View在MVP里应该是一个“极瘦”的概念,最多也只能包含维护自身状态的逻辑,而其它逻辑都应实现在Presenter中。

      总的来说,使用MVP模式可以得到以下两个收益:

      1、将UI和P Logic两个关注点分离,得到更干净和单一的代码结构。

      2、实现了P Logic的复用以及View的无缝替换。

在.NET平台上实现MVP模式

      这一节通过一个示例程序展示在.NET平台上实现MVP的一种实践方法。本来想通过我目前负责的实际项目中的代码片段作为Demo,但这样做存在两个问题:一是这样做可能会违反学校的保密守则,二是这个项目应用了许多其他框架和模式,如通过Unity实现依赖注入,通过PostSharp实现AOP来负责异常处理和事务管理等,通过NHibernate实现的ORM等等,这样如果读者不了解系统整体架构就很难完全读懂代码片段,MVP模式不够突出。因此,我专门为这篇文章实现了一个Demo,其中的MVP实践方式与实际项目中是一致的,而且Demo规模小,排除了其他干扰,使得读者更容易理解其中的MVP实现方式。

      这个简单的Demo运行效果如下:

图3、Demo界面

      这个Demo的功能如下:这是一个简单的点餐软件。系统中存有餐厅所有菜品的信息,客户只需在界面右侧输入菜品名称和数量,单击“添加”按钮,菜品就会被添加到左侧点餐列表,并显示此菜品详细信息。如果所点菜品不存在则软件会给出提示。另外,在左侧已点餐品列表中右键单击某个条目,在弹出菜单中点击“删除”,则可将此菜品从列表删除。

      下面分步骤介绍应用了MVP模式的实现方式。

第一步,解决方法及工程结构

      这个Demo共有三个工程,MVPSimple.Model为Mock方式实现的Services,作为Model;MVPSimple.Presenters为Presenter工程,其中包括Presenter和View Interface;MVPSimple.WinUI为View的Windows Forms实现。

第二步,构建Mock方式的Services

      因为重点在于表示层,所以这里的Services使用了Mock方式,并没有包含真正的业务领域逻辑。其中MVPSimple.Model工程里两个文件的代码如下:

      FoodDto.cs:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System;
 
namespace MVPSimple.Model
{
    /// <summary>
    /// 表示菜品类别的枚举类型
    /// </summary>
    public enum FoodType
    {
        主菜 = 1,
        汤 = 2,
        甜品 = 3,
    }
 
    /// <summary>
    /// 菜品的Data Transfer Object
    /// </summary>
    public class FoodDto
    {
        /// <summary>
        /// ID,标识字段
        /// </summary>
        public Int32 ID { get;set; }
 
        /// <summary>
        /// 菜品名称
        /// </summary>
        public String Name { get;set; }
         
        /// <summary>
        /// 菜品类型
        /// </summary>
        public FoodType Type { get;set; }
 
        /// <summary>
        /// 菜品价格
        /// </summary>
        public Double Price { get;set; }
 
        /// <summary>
        /// 点菜数量
        /// </summary>
        public Int32 Amount { get;set; }
    }
}



      FoodServices.cs:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
using System;
using System.Collections.Generic;
 
namespace MVPSimple.Model
{
    /// <summary>
    /// 菜品Services的Mock实现
    /// </summary>
    public class FoodServices
    {
        private IList<FoodDto> foodList = new List<FoodDto>();
 
        /// <summary>
        /// 默认构造函数,初始化各个菜品
        /// </summary>
        public FoodServices()
        {
            this.foodList.Add(
                new FoodDto()
                {
                    ID = 1,
                    Name = "牛排",
                    Price = 60.00,
                    Type = FoodType.主菜,
                }
            );
 
            this.foodList.Add(
                new FoodDto()
                {
                    ID = 2,
                    Name = "法式蜗牛",
                    Price = 120.00,
                    Type = FoodType.主菜,
                }
            );
 
            this.foodList.Add(
                new FoodDto()
                {
                    ID = 3,
                    Name = "水果沙拉",
                    Price = 58.00,
                    Type = FoodType.甜品,
                }
            );
 
            this.foodList.Add(
                new FoodDto()
                {
                    ID = 4,
                    Name = "奶油红菜汤",
                    Price = 15.00,
                    Type = FoodType.汤,
                }
            );
 
            this.foodList.Add(
                new FoodDto()
                {
                    ID = 5,
                    Name = "杂拌汤",
                    Price = 20.00,
                    Type = FoodType.汤,
                }
            );
        }
 
        /// <summary>
        /// 按照菜品名称获取菜品详细信息
        /// </summary>
        /// <param name="foodName">菜品名称</param>
        /// <returns>含有指定菜品信息的DTO</returns>
        public FoodDto GetFoodDetailByName(String foodName)
        {
            foreach (FoodDto f in this.foodList)
            {
                if (f.Name.Equals(foodName))
                {
                    return f;
                }
            }
 
            return new FoodDto() { ID = 0 };
        }
    }
}



第三步,通过View Interface规定View契约

      如果想实现Presenter和View的交互和无缝替换,必须在它们之间规定一个契约。一般来说,每一张界面(注意是界面不是视图)都应该对应一个View接口,不过由于Demo只有一个页面,所以也只有一个View接口。

      这里需要特别强调,View接口必须抽象于任何具体视图而服务于Presenter,所以,View接口中绝不能出现任何与具体视图相关的元素。例如,我们的Demo中是使用Windows Forms作为视图实现,但View接口中绝不可出现与Windows Forms相耦合的元素,如返回一个Winform的TextBox。因为如果这样做的话,使用其他技术实现的View就无法实现这个接口了,如使用Web Forms实现,而Web Forms是不可能返回一个Winform的TextBox的。

      下面给出视图接口的代码。

      IMainView.cs:

      可以看到,IMainView抽象了如图3所示的界面,但又不包含任何与Windows Forms相耦合的元素,因此如果需要,以后完全可以使用Web Forms、WPF或SL等技术实现这个接口。

第四步,实现Presenter

      上文说过,一个界面应该对应一个Presenter,这个Demo里只有一个界面,所以只有一个Presenter。Presenter仅于视图接口耦合,而并不和具体视图耦合,最好证据就是Presenter工程根本没有引用WinUI工程!代码如下:

      MainPresenter.cs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
using System;
using System.Collections.Generic;
using MVPSimple.Model;
 
namespace MVPSimple.Presenters
{
    /// <summary>
    /// MainView的Presenter
    /// </summary>
    public class MainPresenter
    {
        /// <summary>
        /// 当前关联View
        /// </summary>
        public IMainView View { get;set; }
 
        /// <summary>
        /// 默认构造函数,初始化View
        /// </summary>
        /// <param name="view">MainView对象</param>
        public MainPresenter(IMainView view)
        {
            View = view;
        }
 
        #region Acitons
 
        /// <summary>
        /// Action:将所点菜品增加到点菜列表
        /// </summary>
        public void AddFoodAction()
        {
            if (String.IsNullOrEmpty(View.foodName))
            {
                View.ShowMessage("请选输入菜品名称");
                return;
            }
            if (View.Amount <= 0)
            {
                View.ShowMessage("点菜的份数至少要是一份");
                return;
            }
            if (View.IsExistInList(View.foodName))
            {
                View.ShowMessage(String.Format("菜品【{0}】已经在您的菜单中", View.foodName));
                return;
            }
 
            FoodServices foodServ = new FoodServices();
            FoodDto food = foodServ.GetFoodDetailByName(View.foodName);
            if (food.ID == 0)
            {
                View.ShowMessage(String.Format("抱歉,本餐厅没有菜品【{0}】",View.foodName));
                return;
            }
 
            View.AddFoodToList(food);
        }
 
        /// <summary>
        /// Action:从点菜列表移除某一菜品
        /// </summary>
        /// <param name="foodName">被移除菜品的名称</param>
        public void RemoveFoodAction(String foodName)
        {
            if (View.ShowConfirm("确定要删除吗?"))
            {
                View.RemoveFoodFromList(foodName);
            }
        }
 
        #endregion
    }
}


第五步,实现View

      这里我们使用Windows Forms实现View。如果朋友们有兴趣,完全可以自己试着用Web或WPF实现以下视图,同时可以验证P Logic的可复用性和视图无缝替换,亲身体验一下MVP模式的威力。Winform的View代码如下。

      frmMain.cs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using System;
using System.Windows.Forms;
using MVPSimple.Model;
using MVPSimple.Presenters;
 
namespace MVPSimple.WinUI
{
    /// <summary>
    /// MainView的Windows Forms实现
    /// </summary>
    public partial class frmMain : Form, IMainView
    {
        /// <summary>
        /// 相关联的Presenter
        /// </summary>
        private MainPresenter presenter;
 
        /// <summary>
        /// 默认构造函数,初始化Presenter
        /// </summary>
        public frmMain()
        {
            InitializeComponent();
            this.presenter = new MainPresenter(this);
        }
 
        #region IMainView Members
 
        /// <summary>
        /// View上的菜品名称
        /// </summary>
        public String foodName
        {
            get {return this.tbFoodName.Text; }
            set {this.tbFoodName.Text = value; }
        }
 
        /// <summary>
        /// View上点菜数量
        /// </summary>
        public Int32 Amount
        {
            get {return (Int32)this.tbAmount.Value; }
            set {this.tbAmount.Value = (Decimal)value; }
        }
 
        /// <summary>
        /// 判断某一菜品是否已经存在于点菜列表中
        /// </summary>
        /// <param name="foodName">菜品名称</param>
        /// <returns>结果</returns>
        public bool IsExistInList(String foodName)
        {
            foreach (ListViewItem i in this.lvFoods.Items)
            {
                if (i.Text == foodName)
                {
                    return true;
                }
            }
 
            return false;
        }
 
        /// <summary>
        /// 将某一菜品加入点菜列表
        /// </summary>
        /// <param name="food">菜品DTO</param>
        public void AddFoodToList(FoodDto food)
        {
            ListViewItem item = new ListViewItem();
            Double price = food.Price * (Double)this.tbAmount.Value;
 
            item.Text = food.Name;
            item.SubItems.Add(food.Type.ToString());
            item.SubItems.Add(this.tbAmount.Value.ToString());
            item.SubItems.Add(price.ToString());
            this.lvFoods.Items.Add(item);
        }
 
        /// <summary>
        /// 将某一已点菜品从列表中移除
        /// </summary>
        /// <param name="foodName">欲移除的菜品名称</param>
        public void RemoveFoodFromList(String foodName)
        {
            foreach (ListViewItem i in this.lvFoods.Items)
            {
                if (i.Text == foodName)
                {
                    this.lvFoods.Items.Remove(i);
                }
            }
        }
 
        /// <summary>
        /// View显示提示信息给用户
        /// </summary>
        /// <param name="message">信息内容</param>
        public void ShowMessage(String message)
        {
            MessageBox.Show(message,"信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }
 
        /// <summary>
        /// View显示确认信息并返回结果
        /// </summary>
        /// <param name="message">信息内容</param>
        /// <returns>用户回答是确定还是取消。True - 确定,False - 取消</returns>
        public bool ShowConfirm(String message)
        {
            DialogResult result = MessageBox.Show(message, "确认", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
            return DialogResult.OK == result;
        }
 
        #endregion
 
        #region Event Listeners
 
        private void btnAdd_Click(object sender, EventArgs e)
        {
            this.presenter.AddFoodAction();
        }
 
        private void miDeleteFood_Click(object sender, EventArgs e)
        {
            if (this.lvFoods.SelectedItems.Count != 0)
            {
                String foodName = this.lvFoods.SelectedItems[0].Text;
                this.presenter.RemoveFoodAction(foodName);
            }
        }
 
        #endregion
    }
}



      可以看到,使用了MVP后,View的代码变的非常干净整洁,以前充斥着厚重表示逻辑的事件Listener方法变得“瘦”了许多。

      完成以上几步后,就可以运行这个Demo看效果了。

总结

      这篇文章首先讨论表示层的组成,说明User Interface和Presentation Logic是表示层的两个重要组成部分,并分别说明了两者的作用及交互方式。接着讨论了MVP模式。最后,通过一个Demo展示了在.NET平台上实现MVP的一种实践方式。应该说,MVP很类似简化了MVC,MVP不但可以分离关注、使得代码变得干净整洁、并实现P Logic的复用,而且实现起来比MVC在结构上要简单很多。MVP是一种模式,本身有诸多实现方式,本文只是介绍了笔者使用的一种实践,朋友们也可以在此基础上摸索自己的实践。


PS:

  本文来说比较通俗易懂,对于理解起来也相对容易,想对MVP有更多的了解,请关注我的下一篇文章<.NET平台上的 MVP 模式再探(二)>

面试必问“MVVM模式及与MVP和MVC的区别”的标准答案 什么是MVVM? MVVM是Model-View-ViewModel的简写。微软的WPF带来了新的技术体验,如Silverlight、音频、视频、3D、动画……,这导致了软件UI层更加细节化、可定制化。同时,在技术层面,WPF也带来了 诸如Binding、Dependency Property、Routed Events、Command、DataTemplate、ControlTemplate等新... 阅读详情

相关推荐

掌握MVP模式框架:从理论到实践

MVP(Model-View-Presenter)是一种软件架构模式,特别适用于图形用户界面。它将应用程序划分为三个核心组件:模型(Model)、视图(View)和呈现器(Presenter),目的是实现更清晰的代码分离,便于测试和维护。MVP模式允许视图通过呈现器与模型层交互,确保了视图的整洁和业务逻辑的独立。

weixin_30336531的博客 991

.NET平台上的Model-View-Presenter模式实践

为什么要写这篇文章       笔者当前正在负责研究所中一个项目,这个项目基于.NET平台,初步拟采用C/S部署体系,所以选择了Windows Forms作为其UI。经过几此迭代,我们发现了一个问题:虽然业务逻辑已经封装到Services层中,但诸多的UI逻辑仍然弥漫在各个事件Listener中,使得UI显得臃肿不堪,并且存在诸多重复性代码。另外,需求提供方说,根据实际需要,不排除将部署结构改为...

weixin_34348805的博客 376

MVP模式详解与WinForm实战示例

特性说明职责接收用户输入、更新 Model、选择 View生命周期通常为请求级,每个请求创建一个新的 Controller 实例与 View 的关系View 依赖于 Controller,Controller 决定展示哪个 View与 Model 的关系Controller 调用 Model 方法,处理业务逻辑维度MVCMVPView 是否主动是,View 可以主动绑定数据否,View 是被动的。

weixin_42181686的博客 1291

MVP框架搭建

创建P层RsgisinPresentera接口。创建BaseRetrofitManger管理。记得创建module,首先创建Base层。创建P层ResdistPresenter。创建View:BaseActivity。创建View:BaseFagment。创建Presenter层。可以根据上面的展示图看。创建BaseMode。

zk040221的博客 691

基于MVP模式的WinForms程序开发实战

MVP(Model-View-Presenter)是一种用于分离用户界面(View)、业务逻辑(Presenter)和数据模型(Model)的经典架构模式。它通过引入 Presenter 层替代 MVC 中的 Controller,使视图更加被动化,便于单元测试和维护。MVP中的View通常通过接口来定义,Presenter通过接口与View交互,而不是直接操作控件。这样可以实现View的可替换性和可测试性。set;set;set;对比维度传统方式MVP方式耦合度高。

weixin_42610010的博客 1007

MVP设计模式

MVP设计模式,简捷,实用!

谈谈关于MVP模式中V-P交互问题

在差不多两年的时间内,我们项目组几十来号人都扑在一个项目上面。这是一个基于微软SCSF(Smart Client Software Factory)的项目,客户端是墨尔本一家事业单位。前两周,我奉命负责对某个模块进行Code Review工作,在此期间,发现了一些问题,也有了一些想法。不过,有些想法可能还不是很成熟,不能完全保证其正确性,有机会写出来讨论一下。今天来说说关于MVP的一些想法。 一...

weixin_33754913的博客 301

从FedAvg到实战:手把手教你用PyTorch复现联邦学习经典论文实验(MNIST/CIFAR-10)

本文详细介绍了如何使用PyTorch复现联邦学习经典算法FedAvg在MNIST和CIFAR-10数据集上的实验。从环境搭建、数据非IID划分到模型实现和参数聚合,手把手教你掌握联邦学习的核心技术与实战技巧,帮助开发者和研究人员快速实现隐私保护的分布式机器学习方案。

weixin_29053695的博客 241

.NET平台上的Model-View-Presenter(MVP)模式实践

为什么要写这篇文章       笔者当前正在负责研究所中一个项目,这个项目基于.NET平台,初步拟采用C/S部署体系,所以选择了Windows Forms作为其UI。经过几此迭代,我们发现了一个问题:虽然业务逻辑已经封装到Services层中,但诸多的UI逻辑仍然弥漫在各个事件Listener中,使得UI显得臃肿不堪,并且存在诸多重复性代码。另外,需求提供方说,根据实际需要,不排除将部署结构改为

Percy宣森的博客 1431

ArcGIS中根据DEM和地形起伏度进行地貌分类划分

大家好我是GIS蒲公英,数字高程模型(digital elevation model, DEM)经常都被用到专题地图的底图中,下面来我们来交流一下仅选择海拔和地形起伏度两个指标,用DEM来进行地貌类型的自动判别划分

GIS蒲公英的博客 3万+

# 执行 DISM 命令时出错 87 未在此上下文中识别出 restorehealth 选项。解决方法

# 执行 DISM 命令时出错 87 未在此上下文中识别出 restorehealth 选项。解决方法

段子手168的博客 1万+

.Net平台-MVP模式再探(二)

如果从层次关系来讲,MVP属于Presentation层的设计模式。对于一个UI模块来说,它的所有功能被分割为三个部分,分别通过Model、View和Presenter来承载。Model、View和Presenter相互协作,完成对最初数据的呈现和对用户操作的响应,它们具有各自的职责划分。

Percy宣森 1042

提示词编写:从新手到专家的进阶之路

本文旨在为提示词编写者提供一条从新手到专家的进阶之路。我们将详细探讨提示词编写的背景与意义、基础与进阶技巧、实战应用以及最佳实践。通过逐步深入的分析与实操,读者将掌握提示词编写的核心要点,提高编写效率和作品质量。提示词是用于引导人工智能生成文本的关键词或短语,它包含了作者想要传达的主题、情感和细节。随着人工智能技术的普及,自动化写作工具逐渐应用于各行各业。为了提高写作效率,项目团队决定开发一款基于人工智能的自动化写作工具。成功开发了一款自动化写作工具,生成文本质量较高。

AI天才研究院 1078

MVP模式最佳实践(1)—MVP模式简介

.Net的盛行.Net的出现无疑是一场变革,早期开发需要用C、C++、pascal等编写CGI程序,开发WEB应用,要求的技术门槛极高(当然,我听老一辈说那时的工资也高),使用纯Asp.Net开发WEB程序,门槛低,甚至不用编写任何代码就用实现一个简单的应用(当然,个人觉得这种应用没有什么太大的价值),这给广大的WEB开发者带来了福音,许多人纷纷转向.Net的怀抱,我记得那时在学校,还装模作样

lxxlql的专栏 1181

RFC 字符码转换深度解析:Unicode、非 Unicode 与 MDMP 系统的协奏

老版本 MDMP 系统不上传自定义映射时,Unicode 端会落回“预配置列表”。列表里可见西欧 1100、日文 8000、中文 8400 等经典编号。若需强制覆写映射,可在 SM59 ➡Unicode页签启用,或直接勾选作为兜底。字符编码问题常“潜伏”多年,一旦旧系统升级或新增语种便集中爆发。掌握 RFC code page 转换机理、灵活运用 SM59 选项、在早期对 LANG 字段和字段长度进行治理,是根治乱码与 short dump 的关键。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 207

Linux下驱动开发_块设备驱动开发(硬件上采用SD卡+SPI协议)

这一篇文章将采用SD卡作为存储硬件,利用SPI协议与SD卡通信,完成块设备驱动开发测试。 SD卡可以更加形象的表示块设备开发过程,明白硬件如何交互,完成数据读写。

2594

第一篇:Asp.net MVP模式介绍

作者:BirchLee 2011年8月20日 22时10分【学而不思则罔,思而不学则殆】 本文内容: 1.什么是MVP模式 2.Asp.net MVP模式简单示例 1.什么是MVP模式 任何事物的出现,必有起因。 先看起源:软件工程中一个基本的方法是分层结构。 1.智能用户界面模式(Smart UI Pattern),这种模式下最为经典当属 Winform,用户通过...

axl88896的博客 204

YOLOv11改进策略【Neck】| arXiv 2025 分层多头注意力HMHA 子空间拆分提多样 + 通道重排降冗余,强化目标检测精度

HMHA(Hierarchical Multi-Head Attention,分层多头注意力)旨在解决传统多头注意力(MHA)在图像恢复任务中的核心缺陷——特征冗余。传统MHA将通道均匀拆分给各个头,导致不同头学习的子空间信息相似,进而出现多头关注同一区域、忽略退化区域修复的问题,限制了模型的特征表达能力。HMHA通过“分层子空间拆分+通道重排序”策略,让每个头学习差异化、互补的上下文特征,从根源上缓解冗余,提升图像恢复任务中对复杂退化场景的特征捕捉能力。

Limiiiing的博客 760

MVP开发模式

MVP(Model-View-Presenter)是MVC的改良模式,由IBM的子公司Taligent提出。和MVC的相同之处在于:Controller/Presenter负责业务逻辑,Model管理数据,View负责显示。另外将Controller改名为Presenter,同时改变了通信方向。在MVP模式中,View不再负责同步逻辑,而是由Presenter负责。Presenter既负责业务逻辑也负责同步逻辑。View需要提供操作界面的接口供Presenter调用。

leva的博客 1703

2011-2024年间各省数字普惠金融指数数据(覆盖广度、使用深度和数字化程度)

2、指标:数字金融普惠指数、数字金融覆盖广度指数、数字金融使用深度指数、普惠金融数字化程度指数、数字金融使用深度指数下属的支付、保险、货币基金、信用服务、投资、信贷等分类指数。中国各省的数字普惠金融指数数据,从数字金融覆盖广度、数字金融使用深度和普惠金融数字化程度3个维度来构建数字普惠金融指标体系。数字普惠金融指数一共3个维度、33项具体指标。全套指数包含数字金融覆盖广度指数、数字金融使用深度指数和普惠金融数字化程度指数,以及数字金融使用深度指数下属的支付、保险、货币基金、信用服务、投资、信贷等分类指数。

【CSDN】 1万+

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Asp.net MVP模式介绍

本文内容: 1.什么是MVP模式 2.Asp.net MVP模式简单示例 1.什么是MVP模式 任何事物的出现,必有起因。 先看起源:软件工程中一个基本的方法是分层结构。 1.智能用户界面模式(Smart UI Pattern),这种模式下最为经典当属 Winform,用户通过操作界面操作数据库。 2.理解领域模型(Domain Model)也

vbloveshllm的专栏 1536

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Android应用架构设计—MVP模式

一、前期基础知识储备 随着Android这一移动开发技术的成熟,Android应用架构设计得到了越来越多企业以及开发者的重视,并因此衍生出了Android架构师这一职位。好的架构设计会带来很多好处,比如更易维护、扩展,等等;而差的架构设计或者没有架构设计,则会使得应用在后期的维护和扩展中产生很多严重的问题。目前Android的框架模式主要有MVC、MVP和MVVM,虽说最近流行MVP和MVVM,...

Chin_Style的博客 1428

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

基于MVP模式实现四则运算器

基于MVP模式四则运算器 来到新东家,项目的框架采用的是MVP模式,刚来公司的时候,项目经理给予分配小任务,首先熟悉MVP模式,而后普通的四则运算器的实现使用MVP分层。这里主要回顾当时做任务时候的对于MVP模式的理解。我是一名.Net开发工程师,使用语言为C#。微软在桌面客户端的开发上,提供的是WinForm和WPF两种基于.Net Framework的平台,现在的项目也是主...

dl185249965的博客 324

ASP.NET WebForm MVP(一)- MVP模式简介

在ASP.NET WebForm MVC框架出来之前,.Net方面的Web应用程序的开发主要是基于Web窗体的框架,基于这种框架的优点主要有: 事件模型。 Web 窗体支持与 Windows 应用程序类似的事件驱动编程模式。 许多事件可用,数百个服务器控件对它们提供支持。 状态管理。 Web 窗体通过使用视图状态和基于服务器的控件降低了状态管理的复杂性。 基...

weixin_30896763的博客 207
上一篇: button变成href (即按钮超链效果)
下一篇: 面向接口编程详解(一)——思想基础
Percy__Lee
博客等级 码龄11年 11粉丝 16原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值