前期调研
当当网
例如打开当当网,在购物商城页面可以看到不同类别的商品链接。
通过点击这些链接,将切换到不同的页面展示不同类别的商品,例如我打开图书页面查看图书的商品信息。
在搜索框中搜索商品,当当网会返回查询的结果集,并且显示在网页上。例如我搜索图书《Java 核心技术》,当当网回显了相关商品的商品信息。
把商品放入购物车之后,我们可以对购物车内的商品进行操作,例如增加商品数量、减少商品数量、删除商品、清空购物车和结算。
任务分配
组员 | 任务分配 |
---|---|
林智凯 | 登录系统后端(MySQL + Socket)、购物车类、user 类 |
吴海波 | 登录系统前端、购物车系统 GUI 界面、整合代码 |
韩一佳 | MySQL 数据库设计并连接、商品类设计 |
李存浩 | 商品类及其子类实现 |
登录系统
登录系统将基于三层架构进行设计,使用 Socket 和 MySQL 进行实现,同时使用一定的信息加密提高安全性。详情见Java 程序设计——登录系统。
登录之后将自动进入购物车菜单界面,菜单界面的 GUI 设计如图。
站内短信系统
二层架构
站内短信系统使用的是二层架构,也就是客户端直接向存储层请求数据,存储层更新或检索数据之后进行响应。此处使用 MySQL 数据库作为数据的存储层,这就需要客户端实现对数据库的远程连接。
程序的包结构
商品的类设计
Commodity 类
Commodity 类具有任何商品共有的一些属性,并且提供了属性访问器和修改器。
package logic.product;
/**
* 这是一个关于购物车商品的超类
* @author 李存浩、韩一佳
*
*/
public class Commodity { //商品超类
//private String explain;//商品说明
private Double price;//商品价格
private Integer stock;//商品库存
private Integer number;//商品编号
private String name;//商品名称
private static int temp=0;
private String date;//入库时间
{
temp++;
this.number =temp;
}
/**
*
*/
public Commodity() { //无参构造方法
}
/*
* public Commodity(Double price,Integer stock,String explain,Integer
* number,String date) { this.price=price; this.explain=explain;
* this.stock=stock; this.number=number; this.date=date; }
*/
/**
*
* @param price 商品的价格属性
* @param stock 商品的库存属性
* @param date 商品的入库时间
*/
public Commodity(Double price, Integer stock, String date) { //有参构造函数
this.price = price;
this.stock = stock;
this.date = date;
}
/**
*
* @return 返回值为Double类型,返回值为商品价格.
*/
public Double getPrice() { //返回商品价格
return price;
}
/**
* 这是一个设置商品价格的有参构造方法
* @param price 设置商品的价格,类型为Double
*/
public void setPrice(Double price) { //设置商品价格
this.price = price;
}
/**
*
* @return 返回值类型为Integer,返回值为商品的库存情况
*/
public Integer getStock() { //返回商品库存
return stock;
}
/**
* 这是一个设置商品的初始库存的有参构造方法
* @param stock 设置商品的初始库存
*/
public void setStock(Integer stock) {//设置商品库存
this.stock = stock;
}
/**
*
* @return 返回值为Integer类型,返回值为商品编号.
*/
public Integer getNumber() {//返回商品编号
return number;
}
/**
* 这是一个设置商品编号的有参构造方法
* @param number 设置商品的编号
*/
public void setNumber(Integer number) {//设置商品编号
this.number = number;
}
/**
*
* @return 返回值为String类型,返回值为商品名称.
*/
public String getName() {
return name;
}
/**
* 这是一个设置商品名称的有参构造方法
* @param name 设置商品的名称
*/
public void setName(String name) {
this.name=name;
}
/*
* public String getExplain() { return explain; }
*
* public void setExplain(String explain) { this.explain = explain; }
*/
/**
*
* @return 返回值为String类型,返回值为商品的入库时间.
*/
public String getDate() { //返回商品日期
return date;
}
/**
* 这是一个设置商品入库时间的有参构造方法
* @param 设置商品的入库时间
*/
public void setDate(String date) {//设置商品日期
this.date = date;
}
public String toString() { //将其他类型转化为字符串类型返回
return "Commodity [name="+name+",price=" + price + ", stock=" + stock + ", number=" + number + ", date=" + date + "]";
}
}
Book 类
Book 类继承了 Commodity 类,一个 Book 对象除了要具备商品共有的属性,还需要关注书的作者和出版社等只有书籍商品才具有的属性。
package logic.product;
/**
* 这是有关书籍类商品类的有关的类,为Commodity的子类
* @author 李存浩、韩一佳
*
*/
public class Book extends Commodity {//继承父类:Commodity
private String author;// 作者
private String press;//出版社
/**
* {@inheritDoc} 直接继承父类的属性:price stock date name
* @param author 设置书本的作者名称
* @param press 设置书本的出版社
* @param price 设置书本的价格
* @param stock 设置书本的初始库存
* @param date 设置书本的入库日期
* @param name 设置书本的名称
*/
public Book(String author,String press,Double price,Integer stock,String date,String name) { //有参构造方法
this.author=author;
this.press=press;
super.setPrice(price);//继承父类设置的价格属性
super.setStock(stock);//继承父类设置的商品的库存属性
super.setDate(date);//继承父类设置的商品日期
super.setName(name);//继承父类设置的商品名称
}
/**
*
* @return 返回作者的名称
*/
public String getAuthor() { //设置返回作者的getter方法
return author;
}
/**
* 这是一个设置书本作者名称的有参构造方法
* @param author设置书本作者的名称,类型为String
*/
public void setAuthor(String author) {//设置作者的setter方法
this.author = author;
}
/**
*
* @return 返回书本的出版社
*/
public String getPress() {//设置返回出版社的getter方法
return press;
}
/**
* 这是一个设置书本出版社称的有参构造方法
* @param 设置书本的出版社,类型为String
*/
public void setPress(String press) {//设置出版社的setter方法
this.press = press;
}
/**
*
*/
@Override
public boolean equals(Object obj) {//调用父类的equals方法,返回值为true时,继续比较
if(this==obj)
return true;
if(obj==null) //obj为空值时,返回false
return false;
else {
if(obj instanceof Book) {
Book c=(Book) obj;
if(c.getNumber().equals(this.getNumber())) {//用equals方法比较两者的编号相同时,返回值为true
return true;
}
}
}
return false;//其余返回false
}
/**
* 用toString方法返回值
*/
@Override
public String toString() {//将其他类型转化为字符串类型并返回
return "Book [name="+super.getName()+",author=" + author + ", press=" + press + ", price=" + super.getPrice() + ", stock="+ super.getStock() + ", number=" + super.getNumber() + "]";
}
}
Eproduct 类
Eproduct 类继承了 Commodity 类,一个 Eproduct 对象除了要具备商品共有的属性,还需要关注电器的品牌和型号等只有电器才具有的属性。
package logic.product;
/**
* 这是有关电子产品类商品类的有关的类,为Commodity的子类
* @author 李存浩、韩一佳
*
*/
public class Eproduct extends Commodity{ //电子产品类继承父类Commodity
private String brand;//品牌
private String model_number;//型号
/**
* 这是一个有参构造方法,设定关于Eproduct类的相关属性,且继承了父类的相关属性
* {@inheritDoc} 继承父类的属性有:price stock date
* @param brand 设置电子产品的品牌
* @param price 设置电子产品的价格
* @param stock 设置电子产品的初始库存
* @param explain 填充电子产品的相关说明
* @param date 设置电子产品的入库日期
* @param name 设置电子产品的名称
*/
public Eproduct(String brand,Double price,Integer stock,String explain,String date,String name) {//设置有参构造方法
this.brand=brand;
super.setPrice(price);//继承父类的价格属性
super.setStock(stock);//继承父类的库存属性
super.setDate(date);//继承父类的入库日期属性
super.setName(name);
}
/**
*
* @return 返回电子产品的品牌名称
*/
public String getBrand() {//设置返回品牌的getter方法
return brand;
}
/**
* 这是一个设置电子产品品牌的有参构造方法
* @param brand 设置电子产品的品牌名
*/
public void setBrand(String brand) {//设置品牌的setter方法
this.brand = brand;
}
/**
*
* @return 返回电子产品的型号
*/
public String getModel_number() {//设置返回商品型号的getter方法
return model_number;
}
/**
* 这是一个设置电子产品型号的有参构造方法
* @param model_number 置电子产品的型号
*/
public void setModel_number(String model_number) {//设置电子商品型号的setter方法
this.model_number = model_number;
}
@Override
public boolean equals(Object obj) {
if (this == obj)//通过equals比较,若返回值为真时,继续比较
return true;
if (obj == null)//obj为空时,返回false;
return false;
else {
if (obj instanceof Eproduct) {
Eproduct c = (Eproduct) obj;
if (c.getNumber().equals(this.getNumber())) {//c与obj 用equals方法比较,若c的数量与obj的编号相等时,返回为true
return true;
}
}
}
return false;//其余返回false
}
@Override
public String toString() {//以toString()方法将其他类型转化为字符串类型返回
return "Eproduct [name="+super.getName()+",brand=" + brand + ",price=" + super.getPrice() + ", stock=" + super.getStock() + ", number="+ super.getNumber() + ", date=" + super.getDate() + "]";
}
}
Fruits 类
Fruits 类继承了 Commodity 类,一个 Fruits 对象除了要具备商品共有的属性,还需要关注水果的保质期和产地等只有水果才具有的属性。
package logic.product;
/**
* 这是有关水果类商品类的有关的类,为 Commodity 的子类
* @author 李存浩、韩一佳
*
*/
public class Fruits extends Commodity{//继承父类:Commodity
private Integer period;//记录保质期
private String origin; //产地
public Fruits() { //无参构造方法
}
/**
* {@inheritDoc} 直接继承父类的属性:price stock date name
* @param date 设置水果的入库日期
* @param period 设置水果的保质期
* @param origin 设置水果的生产地
* @param price 设置水果的价格
* @param stock 设置水果的初始库存
* @param name 设置水果的名称
*/
public Fruits(String date, Integer period, String origin, Double price, Integer stock,String name) { //有参构造方法
this.origin = origin; //水果产地
this.period = period; //水果保质期
super.setPrice(price);//设置继承父类的商品价格
super.setStock(stock);//设置继承父类的商品库存
super.setDate(date);//设置继承父类的商品日期
super.setName(name);
}
/**
*
* @return 返回水果的保质期
*/
public Integer getPeriod() {//设置返回保证期的getter方法
return period;
}
/**
* 这是一个设置水果保质期的有参构造方法
* @param period 设置水果的保质期
*/
public void setPeriod(Integer period) {//设置设置保证期的setter 方法
this.period = period;
}
/**
*
* @return 返回水果的生产地
*/
public String getOrigin() {//设置返回水果的产地的getter方法
return origin;
}
/**
*这是一个设置水果生产地的有参构造方法
* @param origin 设置水果的生产地
*/
public void setOrigin(String origin) {//设置水果的产地的setter方法
this.origin = origin;
}
public boolean equals(Object obj) {
if (this == obj) //通过equals比较,若返回值为真时,继续比较
return true;
if (obj == null) //obj为空时,返回false;
return false;
else {
if (obj instanceof Fruits) { //c与obj 用equals方法比较,若c的数量与obj的编号相等时,返回为true
Fruits c = (Fruits) obj;
if (c.getNumber().equals(this.getNumber())) {
return true;
}
}
}
return false;//其余情况返回false
}
@Override
public String toString() {//以toString()方法将其他类型转化为字符串类型返回
return "Fruits [name="+super.getName()+",date=" + super.getDate() + ", period=" + period + ", price=" + super.getPrice() + ", stock="+ super.getStock() + ", number=" + super.getNumber() + ", origin=" + origin + "]";
}
}
购物车的类设计
购物车的状态应当与用户绑定在一起,换言之每个用户都应该有专属的购物车。因此设计思路为每个实例化的用户对象绑定一个购物车类作为其属性,在购物车类中存储商品类对象。
shoppingCar 类
shoppingCar 类基于 HashMap 实现购物车,HashMap 容器以 Commodity 为键,Integer 为值,也就是对某种商品和其数量构成映射。shoppingCar 类实现了加商品、减商品、计算总价和清空的功能。
import java.util.*;
import logic.product.*;
/**
* 购物车类,基于HashMap实现,支持增加和删除商品,计算总价格
* @author 林智凯
* @version 1.0
*/
public class shoppingCar {
private HashMap<Commodity,Integer> shopping_car;
/**
* 这个方法是shoppingCar对象的构造器
*/
public shoppingCar() {
shopping_car = new HashMap<Commodity,Integer>();
}
public HashMap<Commodity,Integer> getShopping_car() {
return shopping_car;
}
/**
* 这个方法将向shoppingCar加入num个a_commodity商品
* @param a_commodity 被增加的商品,Commodity
* @param num 商品加入购物车的数量
*/
public void addCommodity(Commodity a_commodity,int num) {
shopping_car.merge(a_commodity, num, Integer::sum);
}
/**
* 这个方法将向shoppingCar移出num个a_commodity商品
* @param a_commodity 被移出的商品,Commodity
= */
public void reduceCommodity(Commodity a_commodity) {
shopping_car.remove(a_commodity);
}
/**
* 这个方法将计算购物车中的所有商品的总价
* @return 商品的总价,double
*/
public double calculateTotalPrice(){
double sum = 0;
for (Commodity key : shopping_car.keySet()) {
sum = sum + shopping_car.get(key) * key.getPrice();
}
return sum;
}
/**
* 清空购物车,也就是清除所有HashMap映射
*/
public void clearShoppingCar() {
shopping_car.clear();
}
}
Customer 类
同登录系统那篇博客所说,当用户登录成功后,应该把该用户信息实例化一个 Customer 来存储。同时一位用户应该对于一辆购物车,因此实例化 Customer 对象时,应当给用户初始化购物车属性。
import logic.car.*;
/**
* 这个类存储了用户名及其MD5加密,并且将调用UserBehavior类中的方法,实例化后绑定一台购物车给该用户
* @author 林智凯
* @version 1.1
*/
public class Customer {
private final String username;
private final String username_md5;
private shoppingCar shopping_car;
/**
* 这个方法是customer对象的构造器
* @param username 用户名,String
* @return customer对象
*/
public Customer(String username) {
this.username = username;
this.username_md5 = MD5Util.getMD5Str(username);
this.shopping_car = new shoppingCar();
}
/**
* 访问字段username,用户名
* @return username 用户名
*/
public String getUsername() {
return username;
}
/**
* 访问字段username_md,加密后的用户名
* @return username_md 加密后的用户名
*/
public String getUsername_md5() {
return username_md5;
}
/**
* 访问字段shopping_car 绑定给用户的购物车
* @return shopping_car 绑定给用户的购物车
*/
public shoppingCar getShopping_car() {
return shopping_car;
}
}
GUI 设计
打开购物车界面,窗体展示了已经加入购物车中的商品和数量。
点击删除商品,可以将指定商品从购物车中移除。
点击清空购物车,会把所有商品从购物车中移除。
数据库连接并操作
ComDao 接口
由于商品的形式可以是多种数据库或文件,因此定义 ComDao 接口指定了获取商品信息的各种行为。购物车系统中与存储结构交互的行为有 3 种,分别是精确查找、获得结果集和修改库存。
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Vector;
public interface ComDao {
public void connDB() throws Exception;//建立连接
public void closeDB() throws SQLException;//关闭连接
public Vector Find(String find, String table) throws Exception;//精确查找
public ResultSet GetRs(String table) throws Exception;//获得结果集
public void ChangeStock(String table,int ns,int id) throws Exception;//修改库存
}
ComMysql 类
ComMysql 类实现了 ComDao 接口,该类与 MySQL 数据库进行交互,负责从数据库中获取并更新商品信息。
MySQL 数据库设计
books 表
books 表用于存储书籍类商品信息,表具有的字段如图所示。
例如向 books 表中插入 3 个书籍信息。
eproducts 表
eproducts 表用于存储电器类商品信息,表具有的字段如图所示。
例如向 eproducts 表中插入 3 个电子产品信息。
fruits 表
fruits 表用于存储水果类商品信息,表具有的字段如图所示。
例如向 eproducts 表中插入 3 个水果商品信息。
GUI 界面设计
在商城菜单下,选择“书籍”表单时,程序会自动向数据库拉取书籍商品信息。
选择“电子产品”表单时,程序会自动向数据库拉取电器商品信息。
选择“水果”表单时,程序会自动向数据库拉取水果商品信息。
在搜索栏中输入要检索的商品信息,在“搜索结果”表单中回显搜索到的商品信息。
如果搜索了不存在的商品,则输出提示信息。