敏捷软件开发_实例2<四>
敏捷软件开发_实例2<四>
上一章中对薪水支付案例的用例和类做了详细的阐述,在本篇会介绍薪水支付案例包的划分和数据库,UI的设计。
包的划分
一个错误包的划分
为什么这个包是错误的:
- 如果对classifications更改就要影响payrolldatabase更改,还会迫使transactions更改,tansactions重新发布和编译测试就是不负责的,transactions没有共享封闭性,每个类都有自己变化的敏感,所以发布的频率非常高,是不合理的。
调整一下:
将具体类和具体类打包,抽象类和抽象类打包,交互类单独打包。这已经是一个比较好打包设计了。
类的组件应该要符合共同重用原则,payrolldamain中的类没有形成最小的可重用单元,transaction类不必和组件中的其他类一起重用,可以把transaction迁移到transactionapplication类中
这样的划分太精细了,是否有这样的必要需要整体来看。
最终包的结构:
数据库的设计
emplogee是核心
完成这个设计需要进行重构:
提取出payrolldatabase接口,
public interface PayrollDatabase
{
void AddEmployee(Employee employee);
Employee GetEmployee(int id);
void DeleteEmployee(int id);
void AddUnionMember(int id, Employee e);
Employee GetUnionMember(int id);
void RemoveUnionMember(int memberId);
ArrayList GetAllEmployeeIds();
IList GetAllEmployees();
}
内存表实例:
public class InMemoryPayrollDatabase : PayrollDatabase
{
private static Hashtable employees = new Hashtable();
private static Hashtable unionMembers = new Hashtable(); public void AddEmployee(Employee employee)
{
employees[employee.EmpId] = employee;
} // etc...
public Employee GetEmployee(int id)
{
return employees[id] as Employee;
} public void DeleteEmployee(int id)
{
employees.Remove(id);
} public void AddUnionMember(int id, Employee e)
{
unionMembers[id] = e;
} public Employee GetUnionMember(int id)
{
return unionMembers[id] as Employee;
} public void RemoveUnionMember(int memberId)
{
unionMembers.Remove(memberId);
} public ArrayList GetAllEmployeeIds()
{
return new ArrayList(employees.Keys);
} public IList GetAllEmployees()
{
return new ArrayList(employees.Values);
} public void Clear()
{
employees.Clear();
unionMembers.Clear();
}
}
数据库
public class SqlPayrollDatabase : PayrollDatabase
{
private SqlConnection connection; public SqlPayrollDatabase()
{
connection = new SqlConnection("Initial Catalog=Payroll;Data Source=localhost;user id=sa;password=abc");
connection.Open();
} ~SqlPayrollDatabase()
{
connection.Close();
} public void AddEmployee(Employee employee)
{
//增加员工策略
SaveEmployeeOperation operation = new SaveEmployeeOperation(employee, connection);
operation.Execute();
} public Employee GetEmployee(int id)
{
//数据库事务
LoadEmployeeOperation loadOperation = new LoadEmployeeOperation(id, connection);
loadOperation.Execute();
return loadOperation.Employee;
} public void DeleteEmployee(int id)
{
throw new NotImplementedException();
} public void AddUnionMember(int id, Employee e)
{
throw new NotImplementedException();
} public Employee GetUnionMember(int id)
{
throw new NotImplementedException();
} public void RemoveUnionMember(int memberId)
{
throw new NotImplementedException();
} public ArrayList GetAllEmployeeIds()
{
throw new NotImplementedException();
} public IList GetAllEmployees()
{
throw new NotImplementedException();
} }
如果插入雇佣记录成功,但是支付记录失败,为了解决这个问题而使用事务的方式。
public class SaveEmployeeOperation
{
private readonly Employee employee;
private readonly SqlConnection connection; private string methodCode;
private string classificationCode;
private SqlCommand insertPaymentMethodCommand;
private SqlCommand insertEmployeeCommand;
private SqlCommand insertClassificationCommand; public SaveEmployeeOperation(Employee employee, SqlConnection connection)
{
this.employee = employee;
this.connection = connection;
} public void Execute()
{
PrepareToSavePaymentMethod(employee);
PrepareToSaveClassification(employee);
PrepareToSaveEmployee(employee); SqlTransaction transaction = connection.BeginTransaction("Save Employee");
try
{
ExecuteCommand(insertEmployeeCommand, transaction);
ExecuteCommand(insertPaymentMethodCommand, transaction);
ExecuteCommand(insertClassificationCommand, transaction);
transaction.Commit();
}
catch(Exception e)
{
transaction.Rollback();
throw e;
}
} private void ExecuteCommand(SqlCommand command, SqlTransaction transaction)
{
if(command != null)
{
command.Connection = connection;
command.Transaction = transaction;
command.ExecuteNonQuery();
}
} private void PrepareToSaveEmployee(Employee employee)
{
string sql = "insert into Employee values (" +
"@EmpId, @Name, @Address, @ScheduleType, " +
"@PaymentMethodType, @PaymentClassificationType)";
insertEmployeeCommand = new SqlCommand(sql); this.insertEmployeeCommand.Parameters.Add("@EmpId", employee.EmpId);
this.insertEmployeeCommand.Parameters.Add("@Name", employee.Name);
this.insertEmployeeCommand.Parameters.Add("@Address", employee.Address);
this.insertEmployeeCommand.Parameters.Add("@ScheduleType", ScheduleCode(employee.Schedule));
this.insertEmployeeCommand.Parameters.Add("@PaymentMethodType", methodCode);
this.insertEmployeeCommand.Parameters.Add("@PaymentClassificationType", classificationCode);
} private void PrepareToSavePaymentMethod(Employee employee)
{
PaymentMethod method = employee.Method;
if(method is HoldMethod)
methodCode = "hold";
else if(method is DirectDepositMethod)
{
methodCode = "directdeposit";
DirectDepositMethod ddMethod = method as DirectDepositMethod;
insertPaymentMethodCommand = CreateInsertDirectDepositCommand(ddMethod, employee);
}
else if(method is MailMethod)
{
methodCode = "mail";
MailMethod mailMethod = method as MailMethod;
insertPaymentMethodCommand = CreateInsertMailMethodCommand(mailMethod, employee);
}
else
methodCode = "unknown";
} private SqlCommand CreateInsertDirectDepositCommand(DirectDepositMethod ddMethod, Employee employee)
{
string sql = "insert into DirectDepositAccount values (@Bank, @Account, @EmpId)";
SqlCommand command = new SqlCommand(sql);
command.Parameters.Add("@Bank", ddMethod.Bank);
command.Parameters.Add("@Account", ddMethod.AccountNumber);
command.Parameters.Add("@EmpId", employee.EmpId);
return command;
} private SqlCommand CreateInsertMailMethodCommand(MailMethod mailMethod, Employee employee)
{
string sql = "insert into PaycheckAddress values (@Address, @EmpId)";
SqlCommand command = new SqlCommand(sql);
command.Parameters.Add("@Address", mailMethod.Address);
command.Parameters.Add("@EmpId", employee.EmpId);
return command;
} private void PrepareToSaveClassification(Employee employee)
{
PaymentClassification classification = employee.Classification;
if(classification is HourlyClassification)
{
classificationCode = "hourly";
HourlyClassification hourlyClassification = classification as HourlyClassification;
insertClassificationCommand = CreateInsertHourlyClassificationCommand(hourlyClassification, employee);
}
else if(classification is SalariedClassification)
{
classificationCode = "salary";
SalariedClassification salariedClassification = classification as SalariedClassification;
insertClassificationCommand = CreateInsertSalariedClassificationCommand(salariedClassification, employee);
}
else if(classification is CommissionClassification)
{
classificationCode = "commission";
CommissionClassification commissionClassification = classification as CommissionClassification;
insertClassificationCommand = CreateInsertCommissionClassificationCommand(commissionClassification, employee);
}
else
classificationCode = "unknown"; } private SqlCommand CreateInsertHourlyClassificationCommand(HourlyClassification classification, Employee employee)
{
string sql = "insert into HourlyClassification values (@HourlyRate, @EmpId)";
SqlCommand command = new SqlCommand(sql);
command.Parameters.Add("@HourlyRate", classification.HourlyRate);
command.Parameters.Add("@EmpId", employee.EmpId);
return command;
} private SqlCommand CreateInsertSalariedClassificationCommand(SalariedClassification classification, Employee employee)
{
string sql = "insert into SalariedClassification values (@Salary, @EmpId)";
SqlCommand command = new SqlCommand(sql);
command.Parameters.Add("@Salary", classification.Salary);
command.Parameters.Add("@EmpId", employee.EmpId);
return command;
} private SqlCommand CreateInsertCommissionClassificationCommand(CommissionClassification classification, Employee employee)
{
string sql = "insert into CommissionedClassification values (@Salary, @Commission, @EmpId)";
SqlCommand command = new SqlCommand(sql);
command.Parameters.Add("@Salary", classification.BaseRate);
command.Parameters.Add("@Commission", classification.CommissionRate);
command.Parameters.Add("@EmpId", employee.EmpId);
return command;
} private static string ScheduleCode(PaymentSchedule schedule)
{
if(schedule is MonthlySchedule)
return "monthly";
if(schedule is WeeklySchedule)
return "weekly";
if(schedule is BiWeeklySchedule)
return "biweekly";
else
return "unknown";
}
}
界面设计
界面设计时最好使得业务行为和UI分离,这里使用model view presenter模式(MVP)
- model:实体层和数据库交互
- view:界面层
- presenter:业务处理层
MVP的作用是解耦界面、业务和实体的关系
- 在presenter中主动使用view,界面的形态都是由presenter去控制,就是在presenter中去注册view事件,当用户触发事件时,这个事件会通过view传递到presenter中,并通过presenter调用model数据方法,最后presenter 调用引用的view实例去改变界面的形态。
public class AddEmployeePresenter
{
private TransactionContainer transactionContainer;
private AddEmployeeView view;
private PayrollDatabase database;
private int empId;
private string name;
private string address;
private bool isHourly;
private double hourlyRate;
private bool isSalary;
private double salary;
private bool isCommission;
private double commissionSalary;
private double commission;
public AddEmployeePresenter(AddEmployeeView view,
TransactionContainer container,
PayrollDatabase database)
{
this.view = view;
this.transactionContainer = container;
this.database = database;
}
public int EmpId
{
get { return empId; }
set
{
empId = value;
UpdateView();
}
}
public string Name
{
get { return name; }
set
{
name = value;
UpdateView();
}
}
public string Address
{
get { return address; }
set
{
address = value;
UpdateView();
}
}
public bool IsHourly
{
get { return isHourly; }
set
{
isHourly = value;
UpdateView();
}
}
public double HourlyRate
{
get { return hourlyRate; }
set
{
hourlyRate = value;
UpdateView();
}
}
public bool IsSalary
{
get { return isSalary; }
set
{
isSalary = value;
UpdateView();
}
}
public double Salary
{
get { return salary; }
set
{
salary = value;
UpdateView();
}
}
public bool IsCommission
{
get { return isCommission; }
set
{
isCommission = value;
UpdateView();
}
}
public double CommissionSalary
{
get { return commissionSalary; }
set
{
commissionSalary = value;
UpdateView();
}
}
public double Commission
{
get { return commission; }
set
{
commission = value;
UpdateView();
}
}
private void UpdateView()
{
if(AllInformationIsCollected())
view.SubmitEnabled = true;
else
view.SubmitEnabled = false;
}
public bool AllInformationIsCollected()
{
bool result = true;
result &= empId > 0;
result &= name != null && name.Length > 0;
result &= address != null && address.Length > 0;
result &= isHourly || isSalary || isCommission;
if(isHourly)
result &= hourlyRate > 0;
else if(isSalary)
result &= salary > 0;
else if(isCommission)
{
result &= commission > 0;
result &= commissionSalary > 0;
}
return result;
}
public TransactionContainer TransactionContainer
{
get { return transactionContainer; }
}
public virtual void AddEmployee()
{
transactionContainer.Add(CreateTransaction());
}
public Transaction CreateTransaction()
{
if(isHourly)
return new AddHourlyEmployee(
empId, name, address, hourlyRate, database);
else if(isSalary)
return new AddSalariedEmployee(
empId, name, address, salary, database);
else
return new AddCommissionedEmployee(
empId, name, address, commissionSalary,
commission, database);
}
}
public interface ViewLoader
{
void LoadPayrollView();
void LoadAddEmployeeView(
TransactionContainer transactionContainer);
}
public class WindowViewLoader : ViewLoader
{
private readonly PayrollDatabase database;
private Form lastLoadedView;
public WindowViewLoader(PayrollDatabase database)
{
this.database = database;
}
public void LoadPayrollView()
{
PayrollWindow view = new PayrollWindow();
PayrollPresenter presenter =
new PayrollPresenter(database, this);
view.Presenter = presenter;
presenter.View = view; // 相互关联
LoadView(view);
}
public void LoadAddEmployeeView(
TransactionContainer transactionContainer)
{
AddEmployeeWindow view = new AddEmployeeWindow();
AddEmployeePresenter presenter =
new AddEmployeePresenter(view,
transactionContainer, database);
view.Presenter = presenter;
LoadView(view);
}
private void LoadView(Form view)
{
view.Show();
lastLoadedView = view;
}
/// <summary>
/// 最新的form
/// </summary>
public Form LastLoadedView
{
get { return lastLoadedView; }
}
}
public class PayrollMain
{
public static void Main(string[] args)
{
PayrollDatabase database =
new InMemoryPayrollDatabase();
WindowViewLoader viewLoader =
new WindowViewLoader(database);
viewLoader.LoadPayrollView();
Application.Run(viewLoader.LastLoadedView);
}
}
public class PayrollPresenter
{
private PayrollView view;
private readonly PayrollDatabase database;
private readonly ViewLoader viewLoader;
private TransactionContainer transactionContainer;
public PayrollPresenter(PayrollDatabase database,
ViewLoader viewLoader)
{
//this.view = view;
this.database = database;
this.viewLoader = viewLoader;
TransactionContainer.AddAction addAction =
new TransactionContainer.AddAction(TransactionAdded);
transactionContainer = new TransactionContainer(addAction);
}
public PayrollView View
{
get { return view; }
set { view = value; }
}
public TransactionContainer TransactionContainer
{
get { return transactionContainer; }
}
public void TransactionAdded()
{
UpdateTransactionsTextBox();
}
private void UpdateTransactionsTextBox()
{
StringBuilder builder = new StringBuilder();
foreach(Transaction transaction in
transactionContainer.Transactions)
{
builder.Append(transaction.ToString());
builder.Append(Environment.NewLine);
}
view.TransactionsText = builder.ToString();
}
public PayrollDatabase Database
{
get { return database; }
}
public virtual void AddEmployeeActionInvoked()
{
viewLoader.LoadAddEmployeeView(transactionContainer);
}
public virtual void RunTransactions()
{
foreach(Transaction transaction in
transactionContainer.Transactions)
transaction.Execute();
transactionContainer.Clear();
UpdateTransactionsTextBox();
UpdateEmployeesTextBox();
}
private void UpdateEmployeesTextBox()
{
StringBuilder builder = new StringBuilder();
foreach(Employee employee in database.GetAllEmployees())
{
builder.Append(employee.ToString());
builder.Append(Environment.NewLine);
}
view.EmployeesText = builder.ToString();
}
}
public class TransactionContainer
{
public delegate void AddAction();
private IList transactions = new ArrayList();
private AddAction addAction;
public TransactionContainer(AddAction action)
{
addAction = action;
}
public IList Transactions
{
get { return transactions; }
}
public void Add(Transaction transaction)
{
transactions.Add(transaction);
if(addAction != null)
addAction();
}
public void Clear()
{
transactions.Clear();
}
}
public class AddEmployeeWindow : Form, AddEmployeeView
{
public System.Windows.Forms.TextBox empIdTextBox;
private System.Windows.Forms.Label empIdLabel;
private System.Windows.Forms.Label nameLabel;
public System.Windows.Forms.TextBox nameTextBox;
private System.Windows.Forms.Label addressLabel;
public System.Windows.Forms.TextBox addressTextBox;
public System.Windows.Forms.RadioButton hourlyRadioButton;
public System.Windows.Forms.RadioButton salaryRadioButton;
public System.Windows.Forms.RadioButton commissionRadioButton;
private System.Windows.Forms.Label hourlyRateLabel;
public System.Windows.Forms.TextBox hourlyRateTextBox;
private System.Windows.Forms.Label salaryLabel;
public System.Windows.Forms.TextBox salaryTextBox;
private System.Windows.Forms.Label commissionSalaryLabel;
public System.Windows.Forms.TextBox commissionSalaryTextBox;
private System.Windows.Forms.Label commissionLabel;
public System.Windows.Forms.TextBox commissionTextBox;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Label label1;
private System.ComponentModel.Container components = null;
public System.Windows.Forms.Button submitButton;
private AddEmployeePresenter presenter;
public AddEmployeeWindow()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
public AddEmployeePresenter Presenter
{
get { return presenter; }
set { presenter = value; }
}
private void hourlyRadioButton_CheckedChanged(
object sender, System.EventArgs e)
{
hourlyRateTextBox.Enabled = hourlyRadioButton.Checked;
presenter.IsHourly = hourlyRadioButton.Checked;
}
private void salaryRadioButton_CheckedChanged(
object sender, System.EventArgs e)
{
salaryTextBox.Enabled = salaryRadioButton.Checked;
presenter.IsSalary = salaryRadioButton.Checked;
}
private void commissionRadioButton_CheckedChanged(
object sender, System.EventArgs e)
{
commissionSalaryTextBox.Enabled =
commissionRadioButton.Checked;
commissionTextBox.Enabled =
commissionRadioButton.Checked;
presenter.IsCommission =
commissionRadioButton.Checked;
}
private void empIdTextBox_TextChanged(
object sender, System.EventArgs e)
{
presenter.EmpId = AsInt(empIdTextBox.Text);
}
private void nameTextBox_TextChanged(
object sender, System.EventArgs e)
{
presenter.Name = nameTextBox.Text;
}
private void addressTextBox_TextChanged(
object sender, System.EventArgs e)
{
presenter.Address = addressTextBox.Text;
}
private void hourlyRateTextBox_TextChanged(
object sender, System.EventArgs e)
{
presenter.HourlyRate = AsDouble(hourlyRateTextBox.Text);
}
private void salaryTextBox_TextChanged(
object sender, System.EventArgs e)
{
presenter.Salary = AsDouble(salaryTextBox.Text);
}
private void commissionSalaryTextBox_TextChanged(
object sender, System.EventArgs e)
{
presenter.CommissionSalary =
AsDouble(commissionSalaryTextBox.Text);
}
private void commissionTextBox_TextChanged(
object sender, System.EventArgs e)
{
presenter.Commission = AsDouble(commissionTextBox.Text);
}
private void addEmployeeButton_Click(
object sender, System.EventArgs e)
{
presenter.AddEmployee();
this.Close();
}
private double AsDouble(string text)
{
try
{
return Double.Parse(text);
}
catch (Exception)
{
return 0.0;
}
}
private int AsInt(string text)
{
try
{
return Int32.Parse(text);
}
catch (Exception)
{
return 0;
}
}
public bool SubmitEnabled
{
set { submitButton.Enabled = value; }
}
}
完毕
敏捷软件开发_实例2<四>的更多相关文章
- 敏捷软件开发_实例1<二>
敏捷软件开发_实例1 这本书的实例非常好,给了我非常多的启发.主要讲了两个实例,咖啡机和薪水支付实例,咖啡机实例比较简单并没有用什么设计模式,薪水支付实例用了很多设计模式,包括后面的打包等. 咖啡机实 ...
- 敏捷软件开发_设计原则<三>
敏捷软件开发_设计原则 单一职责原则(single responsibilities principle,SRP) 原理:一个类应该只有一个变化 分离职责:如果不耦合的职责那么很简单,如果两个职责耦合 ...
- 敏捷软件开发_UML<一>
敏捷软件开发_UML 所看书籍是:敏捷软件开发_原则.模式与实践_C#版(美)马丁著,这本书写的非常棒,感谢作者.该归纳总结的过程按照我读的顺序写. UML 在建造桥梁,零件,自动化设备之前需要建 ...
- 敏捷软件开发vs传统软件开发
摘要 本文介绍了传统软件开发(着重介绍了传统软件开发中常用的瀑布模型)和敏捷软件开发,以及敏捷开发和传统开发的对比. 一.传统软件开发 比较常用的几种传统软件开发方法:瀑布式开发.迭代式开发.螺旋开发 ...
- 敏捷软件开发:原则、模式与实践——第12章 ISP:接口隔离原则
第12章 ISP:接口隔离原则 不应该强迫客户程序依赖并未使用的方法. 这个原则用来处理“胖”接口所存在的缺点.如果类的接口不是内敛的,就表示该类具有“胖”接口.换句话说,类的“胖”接口可以分解成多组 ...
- 敏捷软件开发:原则、模式与实践——第10章 LSP:Liskov替换原则
第10章 LSP:Liskov替换原则 Liskov替换原则:子类型(subtype)必须能够替换掉它们的基类型(base type). 10.1 违反LSP的情形 10.1.1 简单例子 对L ...
- 敏捷软件开发 Agile software Development(转)
原文链接: http://www.cnblogs.com/kkun/archive/2011/07/06/2099253.html 敏捷软件开发 Agile software Development ...
- 《敏捷软件开发-原则、方法与实践》-Robert C. Martin读书笔记(转)
Review of Agile Software Development: Principles, Patterns, and Practices 本书主要包含4部分内容,这些内容对于今天的软件工程师 ...
- 敏捷软件开发:原则、模式与实践——第13章 写给C#程序员的UML概述
第13章 写给C#程序员的UML概述 UML包含3类主要的图示.静态图(static diagram)描述了类.对象.数据结构以及它们之间的关系,藉此表现出了软件元素间那些不变的逻辑结构.动态图(dy ...
随机推荐
- ASP.NET webform总结
一.asp.net中的内置对象1.Page对象属性:isPostBack 回传 返回bool类型通过url访问就是首次加载,通过控件事件访问页面就是回传.二.页面的执行过程 a.每次访问页面,或访问页 ...
- Java描述设计模式(08):桥接模式
本文源码:GitHub·点这里 || GitEE·点这里 一.桥接模式简介 1.基础描述 桥梁模式是对象的结构模式.又称为柄体(Handle and Body)模式或接口(Interface)模式.桥 ...
- 读书笔记_python网络编程3(5)
5. 网络数据与网络错误 应该如何准备需要传输的数据? 应该如何对数据进行编码与格式化? Py程序需要提供哪些类型的错误? 5.1. 字节与字符串 PC与网卡都支持将字节作为通用传输单元.字节将8比特 ...
- 如何获取数据泵dm和dw进程的 Strace (Doc ID 1411563.1)
How To Get A Strace Of The Data Pump dm And dw Process(es) (Doc ID 1411563.1) APPLIES TO: Oracle Dat ...
- Python对MySql增删改查
pip install pymysql import pymysql db_config = { 'host': '127.0.0.1(ip)', 'port': 3306, 'user': '账号' ...
- CentOS-7-x86_64-Minimal安装后的初始设置
本文是给0基础的初始linux小白写的,只是方便大家尽快上手掌握使用linux系统,完成当前任务,有一定基础能力的请忽略 接上一篇的安装之后,开始配置linx的一些基本功能 1,第一步,也是最重要的一 ...
- [Linux] shell中for循环grep正则统计指定关键字
需求是统计某个业务的访问个数日志服务器上的目录结构是如下,搜索最近7天的指定关键字数据,并排重统计个数: drwxr-xr-x root root Nov : -- drwxr-xr-x root r ...
- go语言设计模式之abstract factory
这个代码太多了,调了一晚上. 只能立图证明我测试通过了哈. 真的是工厂的工厂,有点深.
- Winclone 8 Mac增强汉化版 Windows分区备份迁移工具 8.0.1
winclone 8 for Mac版是一款系统清理工具winclone的Mac平台版本,winclone Mac版可以将Bootcamp分区安装的windows进行克隆也可将克隆文件传回Bootca ...
- Re-py交易
python在线反编译 https://tool.lu/pyc/ 获得源码 import base64 def encode(message): s = '' for i in message: x ...