基本设计模式

it2023-10-16  77

设计模式六大原则

优化代码第一步:单一职责原则让程序更稳定更灵活:开闭原则构建扩展性更好的系统:里式替换原则让项目拥有变化的能力:依赖倒置原则系统有更高的灵活性:接口隔离原则更好地扩展性:迪米特原则

使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性

参考

1. 单例模式:

单例模式是一种常用的软件设计模式,在应用这个模式时,单例类必须自己创建自己的唯一实例,保证一个类仅有一个实例,并提供一个访问它的全局访问点。 优点:

不会频繁地创建和销毁对象,浪费系统资源。避免对资源的多重占用

缺点:

没有接口,不能继承,与单一职责原则冲突

使用场景:IO 、数据库连接、Redis 连接

代码实现:

class Singleton { private static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } } public class Test_Singleton { public static void main(String[] args) { Singleton singleton1 = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); System.out.println(singleton1 == singleton2); //程序的输出结果:true } }

单例延迟加载代码:

// 单例模式-延迟加载版 class SingletonLazy { private static SingletonLazy instance; public static SingletonLazy getInstance() { if (instance == null) { instance = new SingletonLazy(); } return instance; } }

单例模式的线程安全代码:

class SingletonLazy { private static SingletonLazy instance; public static synchronized SingletonLazy getInstance() { if (instance == null) { instance = new SingletonLazy(); } return instance; } }

2. 简单工厂模式

又叫静态工厂方法模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。 比如,一台咖啡机就可以理解为一个工厂模式,你只需要按下想喝的咖啡品类的按钮(摩卡或拿铁),它就会给你生产一杯相应的咖啡,你不需要管它内部的具体实现,只要告诉它你的需求即可。

代码实现

class Factory { public static String createProduct(String product) { String result = null; switch (product) { case "Mocca": result = "摩卡"; break; case "Latte": result = "拿铁"; break; default: result = "其他"; break; } return result; } }

3.抽象工厂模式

在简单工厂的基础上将未来可能需要修改的代码抽象出来,通过继承的方式让子类去做决定 比如,以咖啡工厂为例,口味突然变了,不想喝咖啡了想喝啤酒,这个时候如果直接修改简单工厂里面的代码,这不符合软件设计的“开闭原则”,因为每次新增品类都要修改原来的代码。这个时候就可以使用抽象工厂类了,抽象工厂里只声明方法,具体的实现交给子类(子工厂)去实现,这个时候再有新增品类的需求,只需要新创建代码即可

代码实现

public class AbstractFactoryTest { public static void main(String[] args) { // 抽象工厂 String result = (new CoffeeFactory()).createProduct("Latte"); System.out.println(result); // 输出:拿铁 } } // 抽象工厂 abstract class AbstractFactory{ public abstract String createProduct(String product); } // 啤酒工厂 class BeerFactory extends AbstractFactory{ @Override public String createProduct(String product) { String result = null; switch (product) { case "Hans": result = "汉斯"; break; case "Yanjing": result = "燕京"; break; default: result = "其他啤酒"; break; } return result; } } /\* \* 咖啡工厂 \*/ class CoffeeFactory extends AbstractFactory{ @Override public String createProduct(String product) { String result = null; switch (product) { case "Mocca": result = "摩卡"; break; case "Latte": result = "拿铁"; break; default: result = "其他咖啡"; break; } return result; } }

4.观察者模式

是对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新

优点: 1.观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。 2.由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。如果被观察者和观察者都被扔到一起,那么这个对象必然跨越抽象化和具体化层次。 3.观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知缺点 1.如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2.如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。 3.如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。 4.虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。

代码实现

①.定义观察者(消息接收方)

/* 观察者(消息接收方)*/ interface Observer { public void update(String message); } /* 具体的观察者(消息接收方)*/ class ConcrereObserver implements Observer { private String name; public ConcrereObserver(String name) { this.name = name; } @Override public void update(String message) { System.out.println(name + ":" + message); } }

②.定义被观察者(消息发送方)

/* 被观察者(消息发布方)*/ interface Subject { // 增加订阅者 public void attach(Observer observer); // 删除订阅者 public void detach(Observer observer); // 通知订阅者更新消息 public void notify(String message); } /* 具体被观察者(消息发布方)*/ class ConcreteSubject implements Subject { // 订阅者列表(存储信息) private List<Observer> list = new ArrayList<Observer>(); @Override public void attach(Observer observer) { list.add(observer); } @Override public void detach(Observer observer) { list.remove(observer); } @Override public void notify(String message) { for (Observer observer : list) { observer.update(message); } } }

③.测试

public class ObserverTest { public static void main(String[] args) { // 定义发布者 ConcreteSubject concreteSubject = new ConcreteSubject(); // 定义订阅者 ConcrereObserver concrereObserver = new ConcrereObserver("老王"); ConcrereObserver concrereObserver2 = new ConcrereObserver("Java"); // 添加订阅 concreteSubject.attach(concrereObserver); concreteSubject.attach(concrereObserver2); // 发布信息 concreteSubject.notify("更新了"); //执行结果: //老王:更新了 //Java:更新了 } }

5.代理模式

给某一个对象提供一个代理,并由代理对象控制对原对象的引用

优点 1.代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度; 2.可以灵活地隐藏被代理对象的部分功能和服务,也增加额外的功能和服务。缺点 1.由于使用了代理模式,因此程序的性能没有直接调用性能高; 2.使用代理模式提高了代码的复杂度。

代码实现:

/* 定义租房接口 */ interface IRent { void rent(); } /* 定义买家 */ class Renter implements IRent { @Override public void rent() { System.out.println("我要租房"); } } /* 租房中介 */ class ProxyRent implements IRent { private Renter renter; public ProxyRent() { renter= new Renter(); } //帮买家去买房 @Override public void buy() { renter.buy(); } } /* 代理模式调用 */ public class ProxyTest { public static void main(String[] args) { IRent rents= new ProxyRent(); rents.buy(); } }

动态代理的实现

代码: 1.创建一个接口

public interface UserService { public void add(); public void delete(); public void update(); public void query(); }

2、 为接口创建一个实现类

//真实对象 public class UserServiceImp implements UserService { public void add() { System.out.println("增加一个对象"); } public void delete() { System.out.println("删除一个对象"); } public void update() { System.out.println("更新一个对象"); } public void query() { System.out.println("查询一个对象"); } }

3、 创建代理类实现java.lang.reflect.InvocationHandler接口

public class ProxyInvocationHandler implements InvocationHandler { // 目标对象 private Object target; //绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。 public void setTarget(Object target){ this.target=target; } //动态生成代理对象 //该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 public Object getProxy(){ //第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器 //第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口 //第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法 //根据传入的目标返回一个代理对象 return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } //关联的实现类方法被调用时执行 //InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { getLog(method.getName()); Object result=method.invoke(target,args); return result; } public void getLog(String msg){ System.out.println(msg+"你给我听好了!!"); } }

4、 测试

public class Client { public static void main(String[] args) { UserServiceImp userServiceImp=new UserServiceImp(); ProxyInvocationHandler pih=new ProxyInvocationHandler(); pih.setUserService(userServiceImp); //动态代理代理的是接口 UserService proxy = (UserService) pih.getProxy(); proxy.delete(); proxy.add(); } }

6.适配器模式

适配器模式是将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配而无法一起工作的两个类能够在一起工作

优点: 1.可以让两个没有关联的类一起运行,起着中间转换的作用; 2.灵活性好,不会破坏原有的系统缺点: 过多地使用适配器,容易使代码结构混乱,如明明看到调用的是 A 接口,内部调用的却是 B 接口的实现

代码实现:

/* 传统的充电线 USB */ interface IUSB { void charger(); } /* TypeC 充电口 */ interface ITypeC { void charger(); } class TypeC implements ITypeC { @Override public void charger() { System.out.println("TypeC接口 充电"); } } /* 适配器 */ class AdapterUSB implements IUSB { private TypeC typeC; public AdapterUSB(TypeC typeC) { this.typeC = typeC; } @Override public void charger() { typeC.charger(); } } /* 测试调用 */ public class AdapterTest { public static void main(String[] args) { TypeC typeC = new TypeC(); IUSB USB = new AdapterUSB(typeC); USB.charger(); } }

更多设计模式细节请参考

最新回复(0)