前言:
设计模式,前人总结下留给后人更好的设计程序,为我们的程序代码提供一种思想与认知,如何去更好的写出优雅的代码,23种设计模式,是时候需要掌握它了。
1.工厂模式
大白话:比如你需要一辆汽车,你无需了解汽车是怎么样生产的,你只需要告诉汽车厂,我要买某某某型号的汽车,????,汽车厂造好了给你即可。这就是工厂模式:
隐藏具体对象实例的过程,只需要告诉这个工厂你想要的东西(对象) 它帮你实现,你不必关系具体的实现过程;
举个经常用到的例子:
- 数据库连接当中指明你的数据库类型:mysql
- 数据库分页插件当中的方言
实践:
1、创建一个汽车工厂接口
/** * 汽车生产工厂 */ public interface CarFactory { //创造汽车方法 void createCar(); }
2、使用三个不同的类实现工厂方法
public class DazoCar implements CarFactory { @Override public void createCar() { System.out.println("生产大众汽车"); } }
public class BYDCar implements CarFactory { @Override public void createCar() { System.out.println("生产BYD汽车"); } }
public class BenciCar implements CarFactory { @Override public void createCar() { System.out.println("生产奔驰汽车"); } }
3、创建一个汽车工厂
public class Factory { public static CarFactory getCarFactroy(String type){ //大众 if ("DAZO".equals(type)) { return new DazoCar(); } else if ("BYD".equals(type)) { //BYD return new BYDCar(); } else if ("BENCI".equals(type)){ //奔驰 return new BenciCar(); } return null; } }
4、测试调用
public static void main(String[] args) { CarFactory carFactory = Factory.getCarFactroy("BYD"); carFactory.createCar(); }
这就是一个最简单的工厂模式;
抽象工厂
大白话:比如小米工厂就是一个最大的抽象工厂,它里面不仅有生产手机的工厂,也有生产家用小米电器的工厂,工厂里面套工厂,这就是抽象工厂
就按照这个例子,有一家抽象工厂,它可以有生产手机和生产电器的工厂,它的工厂有具体的实现类(小米或者华为的工厂)
1、创建一个生产手机的工厂
public interface PhoneFactory { //生产方法 void create(); }
2、创建一个生产电器的工厂
//电器工厂,生产电器 public interface DianFactory { //创造电器 void createDian(); }
3、创建一个抽象工厂,这个抽象工厂可以同时生产电器、手机
public abstract class Factory { //获取手机工厂 public abstract PhoneFactory getPhoneFactory(String type); //获取电器工厂 public abstract DianFactory getDianFactory(String type); }
4、具体的抽象工厂继承类 比如小米可以生产、华为也可以生产
//小米工厂 public class XiaoMiFactory extends Factory { @Override public PhoneFactory getPhoneFactory(String type) { return new XiaomiPhone(); } @Override public DianFactory getDianFactory(String type) { return new XiaoMiDian(); } }
小米手机的工厂生产小米手机、小米电器
public class XiaomiPhone implements PhoneFactory { @Override public void create() { System.out.println("小米手机"); } }
public class XiaoMiDian implements DianFactory { @Override public void createDian() { System.out.println("小米生产电器"); } }
同理、华为也可以继承工厂类、它也可以生产手机和电器
public class HuaWeiFactory extends Factory { @Override public PhoneFactory getPhoneFactory(String type) { return new HuaWeiPhone(); } @Override public DianFactory getDianFactory(String type) { return new HuaWeiDian(); } }
public class HuaWeiPhone implements PhoneFactory { @Override public void create() { System.out.println("生产华为手机"); } }
public class HuaWeiDian implements DianFactory { @Override public void createDian() { System.out.println("华为电器"); } }
5.创建一个工厂调度器,来取出需要的工厂
public class FactoryProducer { //按照名称获取工厂 public static Factory getFactory(String name){ if ("XIAOMI".equals(name)) { return new XiaoMiFactory(); } else if ("HUAWEI".equals(name)) { return new HuaWeiFactory(); } return null; } }
最后我们main方法测试,传入HUAWEI, 则调出的是华为工厂,然后调用华为生产手机或者华为生产电器即可
public static void main(String[] args) { Factory factory = getFactory("HUAWEI"); PhoneFactory phoneFactory = factory.getPhoneFactory(null); phoneFactory.create(); }
单例模式
大白话:所谓单例模式,就是这个类在你的系统中只作为一个的存在,是为了防止重复的创造对象、销毁对象所带来的内存的开销。并且在这个类当中提供一个全局访问点
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
举个栗子:
大家都知道我们用SpringBoot作为服务端,向前台返回数据的时候,一般返回的都是JsonObject
这个Object所有的控制器返回数据都可以用到,我们这里就可以考虑用单例模式来初始化这个类,
单例模式在这里分为好几个模块;
- 懒汉式
- 饿汉式(最常用)在初始化类的时候就初始化成员变量
public class JsonObject { private static Map<String,Object> map = new HashMap<>(16); //构造方法私有 private JsonObject(){} //提供全局访问点 synchronized(线程安全) public static synchronized Object resultOk(Object data){ map.clear(); map.put("status","ok"); map.put("data",data); map.put("msg","请求成功"); return map; } //提供全局访问点 public static synchronized Object resultError(String msg){ map.clear(); map.put("status","ok"); map.put("data",null); map.put("msg",msg); return map; } }
我们尝试这调用
public static void main(String[] args) { System.out.println(JsonObject.resultError("密码不能为空")); System.out.println(JsonObject.resultOk("192.168.0.1")); }
这样就完成了一个饿汉式(常用)的一种单例模式
建造者模式
大白话:将一个庞大的系统拆分成小份、小份之间互不影响、小份有者同样的制造过程,这就是建造者模式
举个例子:
我们去肯德基吃快餐, 肯定有它店铺的套餐可以供我们选择,套餐就是庞大的系统,套餐里面最简单的有:汉堡、饮料(组成小份),他们可以任意搭配组成不同的价格
小份有着相同的制造过程,比如汉堡用纸盒包装/饮料用瓶子包装,这里的包装就是建造过程。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
测试代码:
//一个商品的超类接口
//单品接口 public interface Item { //单品名称 String name(); //价格 Double price(); //打包方式 Packing pack(); }
//商品有各自的打包方式 创建一个打包的接口
//包装方式 public interface Packing { //打包方式 String packing(); }
// 汉堡实现商品的接口 并且完善自己的打包方式;
public class Hanbao implements Item { @Override public String name() { return "汉堡"; } @Override public Double price() { return 21.00; } @Override public Packing pack() { return new HeZhuang(); } }
//汉堡是盒装
public class HeZhuang implements Packing { @Override public String packing() { return "盒装"; } }
//可乐实现商品接口
public class Kele implements Item { @Override public String name() { return "可乐"; } @Override public Double price() { return 5.00; } @Override public Packing pack() { return new PingZhuang(); } }
//可乐独有的打包方式
public class PingZhuang implements Packing { @Override public String packing() { return "瓶装"; } }
//建造者模式提供一个菜单组合 用于组合想要的单品组成套餐
public class ItemList { private List<Item> list = new ArrayList<>(); //增加一个套餐 public void addItem(Item item){ list.add(item); } //组合总价 public Double getPrice(){ Double price = 0.00; for (Item item : list) { price+= item.price(); } return price; } //组合详情 public void detail() { for (Item item : list) { System.out.print(item.name()+"-->"); System.out.print(item.price()+"-->"); System.out.print(item.pack().packing()+"-->"); } } }
//开始测试
public class Main { public static void main(String[] args) { System.out.print(no1().getPrice()); no1().detail(); System.out.println(""); no2().detail(); System.out.print(no2().getPrice()); } //套餐一 汉堡2 + 可乐1 public static ItemList no1(){ Hanbao hanbao = new Hanbao(); Hanbao hanbao2 = new Hanbao(); Kele kele = new Kele(); ItemList list = new ItemList(); list.addItem(hanbao); list.addItem(hanbao2); list.addItem(kele); return list; } //套餐二 汉堡1 + 可乐2 public static ItemList no2(){ Hanbao hanbao = new Hanbao(); Kele kele = new Kele(); Kele kele2 = new Kele(); ItemList list = new ItemList(); list.addItem(hanbao); list.addItem(kele2); list.addItem(kele); return list; } }
这里用连个方法组合了两个套餐,这就是建造者模式 ,
原型模式(克隆对象)
大白话:用于创建重复的对象,用克隆对象的方式代替new 关键字的使用。
//对象实现Cloneable 重写父类的clone方法
public class Student implements Cloneable { private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Object clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } }
//测试 分别用new的方式与clone的方式 创建100个相同的对象需要的毫秒数
public static void main(String[] args) { long start = System.currentTimeMillis(); System.out.println(start); for (int i = 0;i<100;i++) { Student student = new Student(); student.setId("1"); student.setName("mrc"); System.out.println(student.getId()); System.out.println(student.getName()); } long end = System.currentTimeMillis(); System.out.println(end); System.out.println("总耗时:"+(end-start)); }
//通过克隆的方式创造相同的对象进行测试
public static void main(String[] args) { long start = System.currentTimeMillis(); System.out.println(start); Student student = new Student(); student.setId("1"); student.setName("mrc"); for (int i = 0;i<100;i++) { Student student1 = (Student) student.clone(); System.out.println(student1.getId()); System.out.println(student1.getName()); } long end = System.currentTimeMillis(); System.out.println(end); System.out.println("总耗时:"+(end-start)); }
果然性能上还是有差距的
适配器模式
大白话:我们现在应用写的接口是TF卡接口 现在我们要用接口对接到电脑的USB上,这时候怎么办,我们就需要一个读卡器(适配器)既有USB接口,又有TF卡接口 这就是一个适配器
这里我们来模拟:定义一个USB接口
public interface USBInterface { //读取 void read(); //写入 void write(); }
//USB2.0实现接口
public class USB20 implements USBInterface { @Override public void read() { System.out.println("USB2.0读取"); } @Override public void write() { System.out.println("USB2.0写入"); } }
//定义一个电脑接口
public interface Computer { //电脑可以读取USB void readUsb(USBInterface usbInterface); }
//用惠普电脑去实现电脑
public class HpComputer implements Computer { @Override public void readUsb(USBInterface usbInterface) { usbInterface.read(); } }
##
这时候就可以测试一个USB在电脑上的读取了
public static void main(String[] args) { USBInterface usb20 = new USB20(); Computer computer = new HpComputer(); computer.readUsb(usb20); }
现在需要把TF卡接入到USB接口上 但是TF接口与USB不适合 需要中间要有一个适配器
//TF卡接口 public interface TF { //读取TF卡 void readTF(); //写入TF卡 void writeTF(); }
//这里需要一个闪迪作为TF卡的实现
public class SanDiskTF implements TF { @Override public void readTF() { System.out.println("闪迪卡读取"); } @Override public void writeTF() { System.out.println("闪迪卡写入"); } }
//接入适配器,把TF卡接入到USB接口上
public class USBAdapterTF implements USBInterface { //将TF卡作为属性引入 private TF tf; public USBAdapterTF(TF tf){ //构造器进行桥接 this.tf = tf; } @Override public void read() { tf.readTF(); } @Override public void write() { tf.writeTF(); } }
//测试使用适配器让电脑读取TF卡
public static void main(String[] args) { Computer computer = new HpComputer(); TF tf = new SanDiskTF(); //USB适配器 USBInterface usbAdapterTF = new USBAdapterTF(tf); //电脑读取 computer.readUsb(usbAdapterTF); }
桥接模式
大白话:我们一般先定义接口,再做实现类对吧,需要加一个接口定义的话,实现类必须实现这个方法,桥接模式就是这两种类型即使改变了结构也不会受到影响。
//定义一个画图的接口
public interface DrawImg { void draw(); }
//再用画出红色去实现这个接口
public class DrawRed implements DrawImg { @Override public void draw() { System.out.println("画出红色"); } }
//用一个中间抽象层代替直接实现 抽象层引用需要实现的方法即可
public abstract class Shape { protected DrawImg drawImg; protected Shape(DrawImg drawImg){ this.drawImg = drawImg; } //抽象衔接 public abstract void draw(); }
//具体的类再去继承这个抽象方法 引用父类里面接口的属性
public class Circle extends Shape { protected Circle(DrawImg drawImg) { super(drawImg); } @Override public void draw() { drawImg.draw(); } }
//实现桥接
public static void main(String[] args) { DrawImg drawImg = new DrawRed(); Circle circle = new Circle(drawImg); circle.draw(); }
过滤器模式(结构型模式)
大白话:通过特定的规则,筛选出一组对象里面符合规则的对象
首先创建一个实体类,用于被筛选的对象。
public class Person { private String name; private String gender; public Person(String name, String gender) { this.name = name; this.gender = gender; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } }
创建一个接口,用于不同的规则区别
//标准接口 public interface Criteria { //传递需要过滤的list List<Person> filtration(List<Person> list); }
//男性规则,从集合中获取出所有男性
public class ManCriteria implements Criteria { @Override public List<Person> filtration(List<Person> list) { List<Person> result = new LinkedList<>(); //循环遍历。拿出男性 list.forEach(temp ->{ if (temp.getGender().equalsIgnoreCase("MAN")) { result.add(temp); } }); return result; } }
女性规则,拿出女性
public class WoManCriteria implements Criteria { @Override public List<Person> filtration(List<Person> list) { List<Person> result = new LinkedList<>(); //循环遍历。拿出女性 list.forEach(temp ->{ if (temp.getGender().equalsIgnoreCase("WOMAN")) { result.add(temp); } }); return result; } }
//开始过滤测试
public static void main(String[] args) { String arr[] = new String[]{"MAN","WOMAN"}; List<Person> people = new ArrayList<>(); for (int i=0;i<10;i++) { Person temp = new Person(); temp.setName(i+""); //随机性别 0-1 int gen = (int) Math.round(Math.random()); temp.setGender(arr[gen]); people.add(temp); } Criteria manCriteria = new ManCriteria(); List<Person> man = manCriteria.filtration(people); print(man); Criteria womanCriteria = new WoManCriteria(); List<Person> woman = womanCriteria.filtration(people); print(woman); } public static void print(List<Person> people){ people.forEach(temp -> { System.out.println("[name="+temp.getName()+",sex="+temp.getGender()+"]"); }); }
进行过滤完成!!
组合模式
理解:一个对象里面含有一个本身的List集合,并且提供了相应的修改相同组的方法
这里按照这个图片来说明:老板下面是财务/人事/研发这些即可看作是一个List,相应的财务组长下面有它所管理的员工,
这就是组合模式,本身含有一个自生对象的List集合
使用:表示复杂的树形图时候。
优点:调用简单/节点可以*增加
不足:例如员工对象里面包含一个员工的LIst,但这个员工对象不是接口,违反依赖倒置原则
public class Employee { //员工名称 private String name; //职位 private String dept; //管辖 private List<Employee> list; public Employee(String name, String dept) { this.name = name; this.dept = dept; this.list = new ArrayList<>(); } //增加一个被管理的员工 public void add(Employee employee) { this.list.add(employee); } //剔除一个 public void remove(Employee employee) { this.list.remove(employee); } //获取员工列表 private List<Employee> getEmployeeList(){ return this.list; } //geter and seter }
//老板下面有两个主管,分别是财务和开发,开发里面管辖了两个员工
public static void main(String[] args) { Employee boss = new Employee("Ms.zhang","老板"); Employee li = new Employee("Ms.li","财务"); Employee kai = new Employee("Ms.long","开发"); boss.add(li); boss.add(kai); Employee mrc = new Employee("Ms.Mrc","员工"); Employee ddl = new Employee("Ms.DDL","员工"); li.add(mrc); li.add(ddl); print(boss); }
持续更新中。。。。
参考:
菜鸟教程:https://www.runoob.com/design-pattern/design-pattern-tutorial.html