您现在的位置是:首页 >技术杂谈 >万字详解设计模式(含真实项目实战)网站首页技术杂谈
万字详解设计模式(含真实项目实战)
设计原则
- 单一责任原则(Single Responsibility Principle):一个类应该只有一个引起变化的原因,即一个类应该只负责一项职责。
- 开闭原则(Open-Closed Principle):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。意味着我们应该通过扩展来实现新的功能,而不是修改已有的代码。
- 里氏替换原则(Liskov Substitution Principle):子类对象可以替换父类对象出现在程序中,而程序的行为不会受到影响。即子类应该能够完全替代父类并保持正确性。
- 依赖倒置原则(Dependency Inversion Principle):高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于具体实现细节,而具体实现细节应该依赖于抽象。
- 接口隔离原则(Interface Segregation Principle):客户端不应该依赖它不需要的接口。一个类不应该被迫实现它用不到的接口方法,应该将接口拆分为更小和更具体的接口。
- 合成/聚合复用原则(Composition/Aggregation Reuse Principle):尽量使用对象组合和聚合,而不是继承关系,以达到代码复用的目的。这种方式更加灵活,减少了类之间的耦合。
- 迪米特法则(Law of Demeter):一个对象应该与其他对象保持最小的相互依赖关系,尽量减少对象之间的交互。一个类应该尽可能少地了解其他类的内部结构。
设计模式
01单例模式
02工厂模式
03构建者模式
04_原型模式
什么是原型模式?
原型模式是一种创建型设计模式,它通过复制现有对象来创建新对象,而不是通过构造函数进行创建。在原型模式中,我们首先创建一个原型对象,然后通过复制原型来创建新的对象。这种方式不仅避免了重复创建相似对象的麻烦,还可以提高对象的创建效率。
原型模式的应用场景
原型模式适用于以下情况:
- 对象的创建成本高昂:如果创建一个对象需要复杂的计算或者与外部资源的交互,那么使用原型模式可以避免重复执行这些操作,提高性能。
- 需要避免构造函数的复杂性:当一个对象的构造函数参数较多或者构造过程比较复杂时,可以使用原型模式来避免构造函数的复杂性。
- 需要保持对象状态的一致性:有时我们需要创建一系列状态相同或相似的对象,使用原型模式可以保持这些对象的状态一致。
原型模式的实现方式
在实现原型模式时,有两种常见的方式:浅拷贝和深拷贝。
浅拷贝(Shallow Copy):浅拷贝会复制对象的所有成员变量,包括基本类型和引用类型。但对于引用类型的成员变量,只复制引用地址,而不复制引用指向的对象本身。这意味着原型对象和复制对象会共享同一个引用对象,对引用对象的修改会影响到原型对象和复制对象。
深拷贝(Deep Copy):深拷贝会复制对象的所有成员变量,包括基本类型和引用类型,并且对于引用类型的成员变量,会递归地进行复制,复制出一个全新的对象。这样原型对象和复制对象就完全独立,互不影响。
下面是以订单生成为例,实现一个基于原型设计模式的示例
浅拷贝
首先,我们定义一个订单类 Order
,它包含了订单的一些基本信息,例如订单号、商品列表和收货地址等。
import java.util.ArrayList;
import java.util.List;
class Order implements Cloneable {
private String orderId;
private List<String> productList;
private String shippingAddress;
public Order(String orderId, List<String> productList, String shippingAddress) {
this.orderId = orderId;
this.productList = productList;
this.shippingAddress = shippingAddress;
}
public String getOrderId() {
return orderId;
}
public List<String> getProductList() {
return productList;
}
public String getShippingAddress() {
return shippingAddress;
}
@Override
public Order clone() throws CloneNotSupportedException {
return new Order(orderId, productList, shippingAddress);
}
}
在上面的示例中,Order
类实现了 Cloneable
接口,并重写了 clone()
方法。在 clone()
方法中,我们使用浅拷贝的方式复制了订单对象,并创建了一个新的订单对象。
接下来,我们可以使用原型对象生成新的订单。下面是一个示例:
import java.util.Arrays;
public class OrderPrototypeExample {
public static void main(String[] args) {
// 创建一个原型订单对象
Order prototypeOrder = new Order("123456", Arrays.asList("Product A", "Product B"), "Shipping Address");
try {
// 克隆原型订单对象生成新的订单
Order newOrder = prototypeOrder.clone();
// 修改新订单的一些属性
newOrder.setOrderId("789012");
newOrder.getProductList().add("Product C");
newOrder.setShippingAddress("New Shipping Address");
// 输出原型订单和新订单的信息
System.out.println("Prototype Order: " + prototypeOrder.getOrderId() + ", " +
prototypeOrder.getProductList() + ", " + prototypeOrder.getShippingAddress());
System.out.println("New Order: " + newOrder.getOrderId() + ", " +
newOrder.getProductList() + ", " + newOrder.getShippingAddress());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
深拷贝
import java.util.ArrayList;
import java.util.List;
class Order implements Cloneable {
private String orderId;
private List<String> productList;
private String shippingAddress;
public Order(String orderId, List<String> productList, String shippingAddress) {
this.orderId = orderId;
this.productList = productList;
this.shippingAddress = shippingAddress;
}
public String getOrderId() {
return orderId;
}
public List<String> getProductList() {
return productList;
}
public String getShippingAddress() {
return shippingAddress;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public void setProductList(List<String> productList) {
this.productList = productList;
}
public void setShippingAddress(String shippingAddress) {
this.shippingAddress = shippingAddress;
}
@Override
public Order clone() throws CloneNotSupportedException {
// 创建新的订单对象
Order clonedOrder = (Order) super.clone();
// 深拷贝商品列表
List<String> clonedProductList = new ArrayList<>();
for (String product : productList) {
clonedProductList.add(product);
}
clonedOrder.setProductList(clonedProductList);
return clonedOrder;
}
}
接下来,我们可以使用原型对象生成新的订单。下面是一个示例:
import java.util.Arrays;
public class OrderPrototypeExample {
public static void main(String[] args) {
// 创建一个原型订单对象
Order prototypeOrder = new Order("123456", Arrays.asList("Product A", "Product B"), "Shipping Address");
try {
// 克隆原型订单对象生成新的订单
Order newOrder = prototypeOrder.clone();
// 修改新订单的一些属性
newOrder.setOrderId("789012");
newOrder.getProductList().add("Product C");
newOrder.setShippingAddress("New Shipping Address");
// 输出原型订单和新订单的信息
System.out.println("Prototype Order: " + prototypeOrder.getOrderId() + ", " +
prototypeOrder.getProductList() + ", " + prototypeOrder.getShippingAddress());
System.out.println("New Order: " + newOrder.getOrderId() + ", " +
newOrder.getProductList() + ", " + newOrder.getShippingAddress());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
我们重写了 clone()
方法,实现了深拷贝。在深拷贝中,除了复制对象的引用外,还会复制对象本身的属性,以确保新对象与原对象完全独立。
在 clone()
方法中,我们首先调用 super.clone()
方法创建一个新的订单对象 clonedOrder
。然后,我们通过遍历原订单对象的商品列表,逐个复制商品到新的列表中,最后设置给新的订单对象。
这样,通过深拷贝,新的订单对象将拥有独立的商品列表,而不会与原订单对象共享相同的引用。
请注意,在深拷贝中,如果订单类中包含其他引用类型的属性,我们需要对这些属性也进行深拷贝,确保新对象中的所有属性都是独立的。
通过使用原型模式,我们可以避免重复创建相似对象的开销,提高代码的效率和性能。同时,原型模式还能保持对象状态的一致性,使得对象的复制更加灵活和可控。
05代理模式
06桥接模式
07装饰模式
08 适配器模式
09 门面模式
10 组合模式
11_享元模式
什么是享元模式?
享元模式是一种结构型设计模式,旨在有效地支持大量细粒度对象的共享。它通过共享对象的公共状态,减少了对象的存储开销,并在需要时动态地提供个性化的特征。这样可以有效地节省内存,并提高系统性能。
享元模式的角色 享元模式中有以下几个角色:
- Flyweight(享元):定义共享对象的接口,包含对共享状态的操作方法。
- ConcreteFlyweight(具体享元):实现享元接口,并实现共享状态的方法。
- FlyweightFactory(享元工厂):负责创建和管理享元对象,提供对共享对象的访问和复用。
电商系统中的享元模式
在电商系统中,优惠券是一种常见的促销工具,用于吸引用户购买商品。每个优惠券都有一些共享的信息,例如优惠券代码、折扣金额和适用范围等。使用享元模式,我们可以共享这些公共信息,并在需要时提供个性化的特征,例如优惠券的有效期和使用限制。
首先,我们定义一个接口 Coupon
,它包含优惠券的公共方法。
public interface Coupon {
void applyCoupon(String productId);
}
然后,我们创建具体的享元类 ConcreteCoupon
,实现了 Coupon
接口。
public class ConcreteCoupon implements Coupon {
private String couponCode;
private double discountAmount;
private String applicableRange;
public ConcreteCoupon(String couponCode, double discountAmount, String applicableRange) {
this.couponCode = couponCode;
this.discountAmount = discountAmount;
this.applicableRange = applicableRange;
}
@Override
public void applyCoupon(String productId) {
System.out.println("Applying coupon " + couponCode + " to product " + productId +
". Discount: " + discountAmount + ", Applicable Range: " + applicableRange);
}
}
接下来,我们创建享元工厂类 CouponFactory
,负责管理和提供享元对象。
import java.util.HashMap;
import java.util.Map;
public class CouponFactory {
private static Map<String, Coupon> coupons = new HashMap<>();
public static Coupon getCoupon(String couponCode, double discountAmount, String applicableRange) {
String key = couponCode + discountAmount + applicableRange;
Coupon coupon = coupons.get(key);
if (coupon == null) {
coupon = new ConcreteCoupon(couponCode, discountAmount, applicableRange);
coupons.put(key, coupon);
}
return coupon;
}
}
在享元工厂中,我们使用一个哈希表 coupons
来存储已创建的优惠券对象。当需要获取优惠券对象时,我们首先检查是否已经存在该优惠券对象,如果存在,则直接返回;如果不存在,则创建一个新的优惠券对象并存储在哈希表中。
现在,我们可以使用享元模式来管理和应用优惠券对象。以下是一个示例:
public class CouponManagementExample {
public static void main(String[] args) {
Coupon coupon1 = CouponFactory.getCoupon("CODE123", 10.0, "Category A");
Coupon coupon2 = CouponFactory.getCoupon("CODE123", 10.0, "Category A");
// coupon1 和 coupon2 引用同一个享元对象,因为它们的公共信息相同
System.out.println(coupon1 == coupon2); // 输出:true
coupon1.applyCoupon("Product A");
coupon2.applyCoupon("Product B");
}
}
在上面的示例中,我们通过 CouponFactory
获取了两个优惠券对象
享元模式 vs 单例
在单例模式中,一个类只能创建一个对象,而在享元模式中,一个类可以创建多个对象,每个对象被多处代码引用共享。实际上,享元模式有点类似于之前讲到的单例的变体:多例。区别两种设计模式,享元模式是为了对象复用,节省内存,而应用多例模式是为了限制对象的个数。
享元模式 vs 缓存
在享元模式的实现中,我们通过工厂类来“缓存”已经创建好的对象。这里的“缓存”实际上是“存储”的意思,跟我们平时所说的“数据库缓存”“CPU 缓存”“MemCache 缓存”是两回事。我们平时所讲的缓存,主要是为了提高访问效率,而非复用。
享元模式 vs 缓存
池化技术中的“复用”可以理解为“重复使用”,主要目的是节省时间(比如从数据库池中取一个连接,不需要重新创建)。在任意时刻,每一个对象、连接、线程,并不会被多处使用,而是被一个使用者独占,当使用完成之后,放回到池中,再由其他使用者重复利用。享元模式中的“复用”可以理解为“共享使用”,在整个生命周期中,都是被所有使用者共享的,主要目的是节省空间。
12 观察者模式
13 模板模式
14 策略模式
15 责任链模式
16_状态模式
什么是状态模式?
状态模式是一种行为型设计模式,它允许对象在内部状态改变时改变它的行为。该模式的核心思想是将不同的状态封装成独立的类,并使这些类实现同一个接口,从而使得状态的变化可以通过切换不同的状态对象来实现。
在订单状态管理中的应用
在电商系统中,订单的状态通常包括已创建、已确认、已支付和已取消等。首先,我们需要定义一个订单状态接口(OrderState
),该接口包含了订单状态的公共方法。
public interface OrderState {
void confirmOrder(Order order);
void cancelOrder(Order order);
void payOrder(Order order);
//其他状态
}
接下来,我们创建具体的订单状态类,实现订单状态接口。我们以订单已创建状态为例进行演示。
public class OrderCreatedState implements OrderState {
@Override
public void confirmOrder(Order order) {
// 订单已创建状态下,确认订单的具体实现
System.out.println("订单已确认");
// 执行订单确认操作,如发送确认邮件、扣减库存等
sendConfirmationEmail(order);
reduceInventory(order);
// 将订单状态设置为已确认状态
order.setState(new OrderConfirmedState());
}
@Override
public void cancelOrder(Order order) {
// 订单已创建状态下,取消订单的具体实现
System.out.println("订单已取消");
// 执行订单取消操作,如释放库存、取消支付等
releaseInventory(order);
cancelPayment(order);
// 将订单状态设置为已取消状态
order.setState(new OrderCancelledState());
}
@Override
public void payOrder(Order order) {
// 订单已创建状态下,支付订单的具体实现
System.out.println("请先确认订单");
}
private void sendConfirmationEmail(Order order) {
// 发送确认邮件的具体实现
System.out.println("发送确认邮件至:" + order.getCustomerEmail());
}
private void reduceInventory(Order order) {
// 扣减库存的具体实现
System.out.println("扣减库存:" + order.getProduct() + " - " + order.getQuantity());
}
private void releaseInventory(Order order) {
// 释放库存的具体实现
System.out.println("释放库存:" + order.getProduct() + " - " + order.getQuantity());
}
private void cancelPayment(Order order) {
// 取消支付的具体实现
System.out.println("取消支付:" + order.getAmount());
}
}
// 其他状态的实现比如:public class OrderCancelState implements OrderState {}
在上述代码中,我们创建了一个OrderCreatedState
类来表示订单已创建状态。在确认订单时,我们执行了一些具体的业务逻辑,如发送确认邮件和扣减库存。在取消订单时,我们释放了库存并取消了支付。这些具体的操作根据实际业务需求来定义。
最后,我们需要在订单类中使用状态模式。订单类(Order
)将包含一个当前订单状态的成员变量,并提供相应的方法来管理订单状态的转换。
public class Order {
private OrderState currentState;
public void setState(OrderState state) {
this.currentState = state;
}
public void confirmOrder() {
currentState.confirmOrder(this);
}
public void cancelOrder() {
currentState.cancelOrder(this);
}
public void payOrder() {
currentState.payOrder(this);
}
// 其他订单相关方法
通过以上设计,我们可以根据具体的业务需求定义和管理订单的不同状态,并在不同状态下执行相应的操作。使用状态模式,我们将不同状态下的行为逻辑封装到各自的状态类中,从而实现代码的解耦和可维护性的提升。
总结
本文介绍了如何使用状态模式来管理订单状态转换。通过将每个状态封装成独立的类并实现同一个接口,我们可以实现订单状态的切换和相应行为的执行。状态模式使得代码更加清晰、可扩展和易于维护。
希望本文能帮助刚入门的读者理解和应用状态模式在订单状态管理中的作用。如果你有任何疑问或需要进一步讨论,欢迎留言。谢谢!