学习设计模式的目的:为了可重用代码,提高代码的可扩展性和可维护性。
也就是说就算不使用也没关系。
本文借鉴了 stackoverflow 上面的一些例子。
单例模式
假设你有一个班级,你需要一个班主任来管理班级的事务。如果每次需要班主任时都要创建一个新的班主任对象,那么就会造成混乱和不一致,而且可能会违反一些规则。如果使用单例模式,就可以保证只有一个班主任对象,你只需要在班级创建时指定一个班主任,然后就可以随时访问他或她。这样就保证了班级的秩序和统一。
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
/**
* 实施双重检查锁定以确保没有两个线程可以同时调用 new Singleton()
*/
public static Singleton getSingleton() {
// Double-check-locking start
synchronized (Singleton.class) {
if (singleton == null) {
// Double-check-locking end
synchronized (Singleton.class) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
工厂模式
一个快餐店,它提供了不同种类的食物,例如汉堡、炸鸡、薯条等。客户端只需要告诉服务员自己想要什么食物,服务员就会根据客户端的选择,从厨房里取出相应的食物给客户端。在这个例子中,快餐店就相当于一个工厂类,它可以根据不同的参数(食物种类),创建并返回不同类型的对象(食物)。客户端不需要知道具体的食物是如何制作的,只需要知道它们都是食物这个接口或抽象类。
public interface PaymentMethod {
public void makePayment();
}
public class CreditCard implements PaymentMethod {
@Override
public void makePayment() {
System.out.println("通过信用卡支付...");
}
}
public class NetBanking implements PaymentMethod {
@Override
public void makePayment() {
System.out.println("通过网络支付...");
}
}
public class PaymentMethodFactory {
public static PaymentMethod getPaymentMethod(String methodName) {
if ("creditcard".equalsIgnoreCase(methodName)) {
return new CreditCard();
} else if ("netbanking".equalsIgnoreCase(methodName)) {
return new NetBanking();
} else {
throw new IllegalArgumentException("您提供的支付方法我们这里暂时不支持喔~~");
}
}
}
public class TestFactory {
public static void main(String[] args) {
PaymentMethod paymentMethod1 = PaymentMethodFactory.getPaymentMethod("creditcard");
PaymentMethod paymentMethod2 = PaymentMethodFactory.getPaymentMethod("netbanking");
paymentMethod1.makePayment();
paymentMethod2.makePayment();
}
}
模板方法模式
假设你要做一顿饭,你需要准备三道菜,分别是红烧肉、清蒸鱼和炒青菜。你可以把做饭的过程抽象成一个模板方法,它包含了以下几个步骤:
洗菜
切菜
炒菜
盛菜
这四个步骤是每道菜都要执行的,但是具体的实现细节会根据不同的菜而有所不同。例如,洗菜的时候,红烧肉要去掉血水,清蒸鱼要去掉鳞和内脏,炒青菜要去掉老叶。切菜的时候,红烧肉要切成小块,清蒸鱼要切成两半,炒青菜要切成段。炒菜的时候,红烧肉要先煎后炖,清蒸鱼要放在蒸锅里蒸,炒青菜要用油爆香蒜末。盛菜的时候,红烧肉要撒上芝麻,清蒸鱼要淋上酱油,炒青菜要放在盘子里。也就是做不同的菜是具体操作是不一样的。
public abstract class Dish {
// 模板方法
public void cook() {
wash();
cut();
fry();
serve();
}
// 基本方法
protected abstract void wash();
protected abstract void cut();
protected abstract void fry();
protected abstract void serve();
}
/**
* 做鱼步骤
*/
public class Fish extends Dish {
@Override
protected void wash() {
System.out.println("洗肉,去掉血水");
}
@Override
protected void cut() {
System.out.println("切肉,切成小块");
}
@Override
protected void fry() {
System.out.println("煎肉,炖肉,加酱油,加糖,加料酒");
}
@Override
protected void serve() {
System.out.println("盛肉,撒上芝麻,香喷喷的红烧肉就做好了");
}
}
/**
* 做菜步骤
*/
public class Vegetable extends Dish {
@Override
protected void wash() {
System.out.println("洗菜,去掉老叶");
}
@Override
protected void cut() {
System.out.println("切菜,切成段");
}
@Override
protected void fry() {
System.out.println("热油,爆香蒜末,下菜,翻炒,加盐");
}
@Override
protected void serve() {
System.out.println("盛菜,放在盘子里,清爽开胃的炒青菜就做好了");
}
}
public class Test {
public static void main(String[] args) {
// 做清蒸鱼
Dish fish = new Fish();
fish.cook();
// 做炒青菜
Dish vegetable = new Vegetable();
vegetable.cook();
}
}
注册器模式
我们有很多不同的电器,比如电视、冰箱、空调等,它们都有各自的功能和用途。假如我们有一个万能遥控,我们可以把它们都注册到一个遥控器类中,然后通过遥控器来控制它们的开关、调节温度、切换频道等操作。这样就可以避免直接操作每个电器,而是通过一个统一的接口来管理它们。
//定义一个电器接口,有开关和调节功能
public interface Appliance {
void turnOn();
void turnOff();
void adjust(int level);
}
public class TV implements Appliance {
private int channel;
private boolean isOn;
public TV() {
channel = 1;
isOn = false;
}
@Override
public void turnOn() {
isOn = true;
System.out.println("TV is on");
}
@Override
public void turnOff() {
isOn = false;
System.out.println("TV is off");
}
@Override
public void adjust(int level) {
if (isOn) {
channel = level;
System.out.println("TV channel is " + channel);
}
}
}
public class Fridge implements Appliance {
private int temperature;
private boolean isOn;
public Fridge() {
temperature = 5;
isOn = false;
}
@Override
public void turnOn() {
isOn = true;
System.out.println("Fridge is on.");
}
@Override
public void turnOff() {
isOn = false;
System.out.println("Fridge is off.");
}
@Override
public void adjust(int level) {
if (isOn) {
temperature = level;
System.out.println("Fridge temperature is " + temperature + "°C.");
}
}
}
public class Registry {
// 使用一个哈希表来储存电器实例和他们的别名
public static Map<String, Appliance> appliances = new HashMap<>();
static {
// 初始化哈希表
// 将两个电器实例注册到注册器中
appliances.put("tv", new TV());
appliances.put("fridge", new Fridge());
}
// 获取电器实例,如果没有则返回Null
public static Appliance get(String name) {
return appliances.getOrDefault(name, null);
}
}
public class RemoteControl {
public static void turnOn(String alias) {
Appliance appliance = Registry.get(alias);
if (appliance != null) {
appliance.turnOn();
} else {
System.out.println("No such appliance.");
}
}
public static void turnOff(String alias) {
Appliance appliance = Registry.get(alias);
if (appliance != null) {
appliance.turnOff();
} else {
System.out.println("No such appliance.");
}
}
public static void adjust(String alias, int level) {
Appliance appliance = Registry.get(alias);
if (appliance != null) {
appliance.adjust(level);
} else {
System.out.println("No such appliance.");
}
}
}
public class Test {
public static void main(String[] args) {
RemoteControl.turnOn("tv");
RemoteControl.adjust("tv", 5);
RemoteControl.turnOff("tv");
System.out.println("-----");
RemoteControl.turnOn("fridge");
RemoteControl.adjust("fridge", 6);
RemoteControl.turnOff("fridge");
}
}
策略模式
假设你是一名厨师,你正在烹饪一道美食,你需要根据不同的食材和口味来选择使用不同的烹饪方法。例如,对于一块牛排,你可以选择煎、烤、烤箱或烤肉机等不同的烹饪方法,这些烹饪方法就是不同的策略。在策略模式中,你可以将每种烹饪方法封装在独立的策略类中,例如,煎牛排策略类、烤牛排策略类等。然后,你可以在上下文中选择不同的策略类来完成烹饪。例如,如果你想煎牛排,你可以将煎牛排策略类传递给上下文,上下文将使用该策略类中的算法来煎牛排。
// 定义烹饪策略接口类
public interface CookingStrategy {
void cook(String food);
}
// 油煎实现类
public class FryCookingStrategy implements CookingStrategy{
@Override
public void cook(String food) {
System.out.println("使用油煎的方式烹饪" + food);
}
}
// 烤箱实现类
public class BakeCookingStrategy implements CookingStrategy {
@Override
public void cook(String food) {
System.out.println("使用烤箱烤的方式烹饪" + food);
}
}
// 上下文对象
public class CookingContext {
private CookingStrategy cookingStrategy;
public CookingContext(CookingStrategy cookingStrategy) {
this.cookingStrategy = cookingStrategy;
}
public void cook(String food) {
cookingStrategy.cook(food);
}
}
// 测试
public class Client {
public static void main(String[] args) {
new CookingContext(new FryCookingStrategy()).cook("牛排");
new CookingContext(new BakeCookingStrategy()).cook("猪排");
}
}
装饰者模式
假设你要买一杯咖啡,你可以选择不同的配料,比如牛奶、糖、摩卡、奶泡等。每种配料都有自己的价格和名称,你可以根据自己的喜好来组合不同的配料。你可以用装饰者模式来实现这个功能,步骤如下:
定义一个抽象的咖啡接口,它有两个方法:获取价格和获取名称。
定义一个具体的咖啡类,它实现了咖啡接口,表示一杯普通的咖啡。
定义一个抽象的装饰类,它也实现了咖啡接口,并且持有一个咖啡对象的引用。
定义多个具体的装饰类,它们继承了抽象的装饰类,并且重写了咖啡接口的方法,在原有的价格和名称上加上自己的价格和名称。
在客户端,你可以创建一个咖啡对象,并且用不同的装饰类来包装它,从而得到一杯自定义的咖啡
// 定义咖啡接口
public interface Coffee {
// 获取价格
int getPrice();
// 获取名称
String getName();
}
// 具体的纯咖啡
public class SimpleCoffee implements Coffee {
private int price = 20;
private String name = "纯咖啡";
@Override
public int getPrice() {
return price;
}
@Override
public String getName() {
return name;
}
}
// 咖啡装饰器
public abstract class CoffeeDecorator implements Coffee{
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public int getPrice() {
return coffee.getPrice();
}
@Override
public String getName() {
return coffee.getName();
}
}
public class MilkDecorator extends CoffeeDecorator{
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public int getPrice() {
return coffee.getPrice() + 2;
}
@Override
public String getName() {
return coffee.getName() + " 加牛奶";
}
}
// 加糖
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public int getPrice() {
return coffee.getPrice() + 3;
}
@Override
public String getName() {
return coffee.getName() + " 加糖";
}
}
// 测试
public class CLient {
public static void main(String[] args) {
// 创建一个普通的咖啡对象
Coffee coffee = new SimpleCoffee();
System.out.println(coffee.getPrice());
System.out.println(coffee.getName());
// 加牛奶
coffee = new MilkDecorator(coffee);
System.out.println(coffee.getPrice());
System.out.println(coffee.getName());
// 加糖
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getPrice());
System.out.println(coffee.getName());
}
}
观察者模式
假设你是一名老师,你想要知道你的学生们是否理解了你的课程内容。你可以让每个学生成为一个观察者,而你自己成为主题。
当你讲课时,你的学生们会注意听你的讲解。一旦你讲解到某个重点时,你可以选择向所有学生发出一个信号,告诉他们这个重点非常重要。这个信号可以是你的语气变化、表情或者是举手示意等方式。这些观察者会接收到你的信号并作出相应的反应,例如记笔记、提问或者发表评论。当你讲解到下一个重点时,你再次发出信号,所有的观察者都会收到通知并做出相应的反应。这样,你就可以通过观察者模式及时了解学生们对你的课程理解的情况,及时调整你的教学方式。这里的老师就是被观察者,学生就是观察者。
// 观察者接口
public interface Observer {
void update(String signal);
}
// 定义主题接口
public interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers(String signal);
}
// 定义老师类,实现主题接口
public class Teacher implements Subject{
private List<Observer> observers = new ArrayList<>();
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(String signal) {
for (Observer observer : observers) {
observer.update(signal);
}
}
// 讲解课程内容
public void teach() {
System.out.println("老师正在讲解课程内容...");
// 当讲解到重点时,发出信号通知观察者
String signal = "鸡汤来喽";
notifyObservers(signal);
System.out.println("老师讲解结束。");
}
}
// 定义学生类,实现观察者接口
public class Student implements Observer{
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void update(String signal) {
System.out.println(name + "收到信号:" + signal);
}
}
// 测试
public class Demo {
public static void main(String[] args) {
// 创建学生和老师
Teacher teacher = new Teacher();
Student s1 = new Student("小明");
Student s2 = new Student("小红");
// 学生需要订阅老师
teacher.attach(s1);
teacher.attach(s2);
// 老师讲解课程内容
teacher.teach();
// 学生取消订阅
teacher.detach(s1);
teacher.detach(s2);
}
}
迭代器模式
假设你有一个购物清单,其中列出了你要买的商品。你可以使用迭代器模式来访问购物清单中的每个商品。java 中已经帮我们封装好了迭代器方法,平时也不用我们自己实现,直接使用即可。
public class IteratorPatternDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("电脑");
list.add("显示器");
list.add("键盘");
// 获取迭代器对象
Iterator<String> iterator = list.iterator();
// 遍历容器中的元素
while (iterator.hasNext()) {
String device = iterator.next();
System.out.println(device);
}
}
}
适配器模式
假设你拥有一款新手机,它只能使用 USB-C 接口进行充电。但是你家里的充电器都是旧的 Micro-USB 接口,它们的接口不兼容。这时,你需要一种适配器,将 Micro-USB 接口转换为 USB-C 接口,以使得新手机可以使用旧的充电器进行充电。
适配器的工作原理是,将旧接口的输入转换为新接口的输出,这样就可以使用旧的充电器给新手机充电。同样,适配器模式中的适配器类也是将一个类的接口转换为另一个类的接口,以便客户端代码可以使用它们。
// 客户端期望的接口
public interface USB_C {
void changeWithUSB_C();
}
// 不兼容的旧接口
public interface Micro_USB {
void changeWithMicro_USB();
}
// 旧接口的实现类
public class OldChanger implements Micro_USB{
@Override
public void changeWithMicro_USB() {
System.out.println("使用Micro_USB接口充电中");
}
}
// 适配器类,将旧接口转换为新接口
public class USB_C_Adapter implements USB_C{
private Micro_USB micro_usb;
public USB_C_Adapter(Micro_USB micro_usb) {
this.micro_usb = micro_usb;
}
@Override
public void changeWithUSB_C() {
System.out.println("使用USB_C适配器");
// 调用旧接口
micro_usb.changeWithMicro_USB();
}
}
// 测试
public class Client {
public static void main(String[] args) {
OldChanger oldChanger = new OldChanger();
USB_C_Adapter adapter = new USB_C_Adapter(oldChanger);
adapter.changeWithUSB_C();
}
}
门面模式
当我们想要吃饭的时候,我们不需要自己买菜、洗菜、做饭、洗盘子等一系列的步骤,我们只需要去餐厅点菜就可以了。餐厅就相当于一个门面,它为我们提供了一个简单的接口,隐藏了后台的复杂过程。即提供一个简单的接口,让客户可以通过这个接口完成买菜、洗菜、做饭、洗盘子等操作,这样,客户只需要记住一个接口,就可以完成所有的操作,这大大降低了客户的使用难度
// 买菜
public class BuyVegetables {
public void buy() {
System.out.println("买菜");
}
}
// 洗菜
public class WashVegetables {
public void wash() {
System.out.println("洗菜");
}
}
// 做饭
class Cook {
public void cook() {
System.out.println("做饭");
}
}
// 洗盘子
class WashDishes {
public void wash() {
System.out.println("洗盘子");
}
}
// 门面类
public class Restaurant {
private BuyVegetables buyVegetables;
private WashVegetables washVegetables;
private WashDishes washDishes;
private Cook cook;
public Restaurant() {
buyVegetables = new BuyVegetables();
washVegetables = new WashVegetables();
cook = new Cook();
washDishes = new WashDishes();
}
// 统一的方法
public void eat() {
buyVegetables.buy();
washVegetables.wash();
cook.cook();
washDishes.wash();
}
}
// 测试
public class Client {
public static void main(String[] args) {
Restaurant restaurant = new Restaurant();
restaurant.eat();
}
}