一、简单工厂模式
在实例化对象时,通常使用new,但是这样耦合度就比较高。工厂模式就是将构造对象的过程抽象为一个类,实现解耦。简单工厂是面向过程到面向对象的抽象,使用者不关心创建过程。
二、工厂方法模式
简单工厂的升级版,创建对象的方法抽象为抽象方法,子类决定要实例化的类型。将对象的实例化推迟到子类。
三、抽象工厂模式
抽象工厂是工厂方法的升级,抽象工厂模式中可以定义实现多个接口,一个工厂可以生成多个产品类。
如一个工厂可以使用同种原料产出轿车、suv,一个工厂可以产出各种品牌的沙发和座椅;
例:一个工厂生产不同品牌电脑的配件
// 创造配件的抽象 public abstract class AbstractFactory{ // RAM public abstract void createRAM(); // CPU public abstract void createCPU(); // Mouse public abstract void createMouse(); } // 配件的配置抽象 public abstract class RAM{ public abstract void showRAM(); } public abstract class CPU{ public abstract void showCPU(); } public abstract class Mouse{ public abstract void showMouse(); } // 具体工厂--华为实现 public class HWFactory extends AbstractFactory{ @Override public void createRAM(){ new HWRAM().showRAM(); } @Override public void createCPU(){ new HWCPU().showCPU(); } @Override public void createMouse(){ new HWMouse().showMouse(); } } // 具体工厂--小米实现 public class XMFactory extends AbstractFactory{ @Override public void createRAM(){ new XMRAM().showRAM(); } @Override public void createCPU(){ new XMCPU().showCPU(); } @Override public void createMouse(){ new XMMouse().showMouse(); } } // 具体品牌的配件配置实现 public class HWRAM extends RAM{ @Override public void showRAM(){ System.out.println("HW RAM"); } } public class HWCPU extends CPU{ @Override public void showCPU(){ System.out.println("HW CPU"); } } public class HWMouse extends Mouse{ @Override public void showMouse(){ System.out.println("HW Mouse"); } }
四、建造者模式:将产品和建造过程解耦
很多产品的建造流程一样,可以抽象
Product(产品角色):一个具体的产品对象
Builder(抽象建造者):创建一个Product对象的各个部件指定的接口/抽象类
ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件
Director(指挥者):构建一个使用Builder接口的对象。主要作用:一是隔离客户与对象的生产过程,二是:负责控制产品对象的生产过程。
指挥者向外对接诉求,接收业务,给出产品
例:经理说要请大家吃奶茶(product),于是选择一家奶茶店茶百道(Builder),你就去店里找到店长(Director)说了清单,店长让你稍等十分钟;接着店长找到两位店员(ConcreteBuilder)安排下去各种口味及注意事项与时间要求,不一会儿在店长的指挥下奶茶好了,你接到通知今天不喝奶茶了哦(完蛋)。
// 指挥者向外对接诉求,接收业务,给出产品 public abstract class TeaBuilder{ protected Tea tea = new Tea(); public abstract void buildIce(); public abstract void buildMilk(); public Tea build(){ return tea; } } public class Director { TeaBuilder teaBuilder; public Director(TeaBuilder teaBuilder){ this.teaBuilder = teaBuilder; } public Tea constructTea(){ // 先加冰 teaBuilder.buildIce(); // 再加牛奶 teaBuilder.buildMilk(); return teaBuilder.build(); } } public class ChaBaiDaoTea extends TeaBuilder{ @Override public void buildIce(){ tea.setTemperature(5); } @Override public void buildMilk(){ tea.setTaste("milk"); } } public static void main(String[] args){ TeaBuilder chaBaiDaoBuilder = new ChaBaiDaoTea(); Director director = new Director(chaBaiDaoBuilder); Tea tea = director.constructTea(); }
五、桥接模式:提高系统的可扩充性
角色如下:
抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
拓展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过聚合关系调用实现角色中的业务方法。
实现化(Implementor)角色:定义实现化角色,供拓展抽象化角色调用。
具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
当一个类存在两个独立变化维度,且都需要扩展时,桥接模式可以使它们在抽象层建立一个关系(对象和对象行为独立变化,组合一起配合使用)。通常,将类的一些普通业务方法和与之关系最密切的维度设计为“抽象类”(抽象部分),将另一个维度设计为“实现类”层次(实现部分)。
例:开发一个跨平台的视频播放器,可在不同操作平台上播放多种格式视频文件。(两个维度:平台、格式)
// 用心体会实现和继承,抽象的初心 // 视频文件,实现化角色 public interface VideoFile{ // 解码功能 void decode(String fileName); } // avi文件,具体实现化角色 public class AVIFile implements VideoFile{ @Override public void decode(String fileName){ System.out.println("avi文件:"+fileName) } } // rmvb文件,具体实现化角色 public class RMVBFile implements VideoFile{ @Override public void decode(String fileName){ System.out.println("avi文件:"+fileName) } } // 操作系统版本,拓展抽象化角色 public abstract class OperatingSysVersion{ // 聚合实现化对象 protected VideoFile videoFile; public OperatingSysVersion(VideoFile videoFile){ this.videoFile = videoFile; } // 播放文件的方法 public abstract void play(String fileName); } // windows版本 public class Windows extends OperatingSystem{ public Windows(VideoFile videoFile){ super(videoFile); } public void play(String fileName){ videoFile.decode(fileName); } } // mac版本 public class Mac extends OperatingSystem{ public Mac(VideoFile videoFile){ super(videoFile); } public void play(String fileName){ videoFile.decode(fileName); } } // 测试类 public static void main(String[] args){ OperatingSystem os = new Mac(new AVIFile()); os.play("功夫"); }
六、组合模式
Component:组合中对象声明接口,用于访问和管理Component子部件。Component可以是抽象类或者接口。
Leaf:组合中叶子节点,没有子节点。
Composite:非叶子节点,用于存储子部件,在Component接口实现子部件的相关操作,如操作删除行为。
// 文件夹的经典案例 // 抽象代码如下 // 抽象构件 public abstract class Component{ //增加 public void add(Component c){ throw new UnsuportedOperationException(); } // 删除 public void remove(Componnet c){ throw new UnsupportedOperationException(); } //获取成员 public Component getChild(int i){ throw new UnsupportedOperationException(); } //业务方法 public void operaion(){ throw new UnsupportedOperationException(); } } //叶子构件--只需实现业务方法,其他的默认继承 public class Leaf extends Component{ @Override public void operation(){ System.out.println("叶子节点") } } //容器构件 public class Composite extends Component{ private List<Component> list = new ArrayList<Component>(); @Override public void add(Component c){ list.add(c); } @Override public void remove(Component c){ list.remove(c); } @Override public Component getChild(int i){ return list.get(i); } @Override public void operation(){ for(Component child: list){ child.operation(); } } }
容器构件中实现了抽象构件中声明的所有方法,因为组合模式中,有多层嵌套,需要在容器构件中operation()方法中递归调用成员构件的操作方法。
七、适配器模式
适配器模式:将一个类的接口转换为另一个接口,兼容新的新类。
使用适配器做个中转,将用户和被调用者解耦。
// MP4转为MP3格式 // 目标角色MP3 public class MP3{ String getMP3(){ return "MP3"; } } //源角色MP4 public interface MP4{ String getMP4(); } // eg1 适配器Adapter,继承的依赖强,还可以使用聚合 public class MP4Adapter extends MP3 impl MP4{ @Override public String getMP4(){ return this.getMP3(); } } // eg2 public class MP4Adapter impl MP4{ private MP3 mp3; public MP4(MP3 mp3){ this.mp3 = mp3; } @Override public String getMP4(){ return mp3.getMP3(); } }
八、装饰者模式
装饰者模式动态地将责任附加到对象上。若要扩展功能,装置者提供了比继承更有弹性的替代方案。
主要角色:
抽象构件(Component):定义一个抽象接口以规范接收附加责任的对象。
具体构件(ConcreteComponent):实现抽象构件,通过装饰角色为其添加一些职责。
抽象装饰(Decorator):继承抽象构件,并包含具体构件的实例,可以通过其子类拓展具体构件的功能。
具体装饰(ConcreteDecorator):实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
// 例:奶茶店买奶茶 // 抽象构件 public abstract class Tea{ // 每种饮品的名称 public String description = "就是白开水兑科技"; private double price = 0.0f; public String getDescription(){ return description; } public double getPrice(){ return price; } public void setDescription(String des){ this.description = des; } public void setPrice(double price){ this.price = price; } // 每种饮品的价格不同,定义为抽象方法 public abstract double setPrice(); } // 奶茶--具体构件 public class MilkTea extends Tea{ public MilkTea(){ setDescription("奶茶"); setPrice(10); } @Override public double cost(){ return super.getPrice(); } } // 果茶--具体构件 public class FruitTea extends Tea{ public FruitTea(){ setDescription("果茶"); setPrice(15); } @Override public double cost(){ return super.getPrice(); } } // 抽象装饰-继承抽象构件 public class materialDecorator extends Tea{ // 装饰者聚合,关联一种饮品,以便附加新的行为 private Tea tea; public material(Tea tea){ this.tea = tea; } @Override public Double cost(){ return getPrice() + tea.getPrice(); } @Override public String getDescirption(){ return des + ""+getPrice()+ ","+tea.getDescription(); } } // 红豆-具体装饰 public class RedBean extends materialDecorator{ public RedBean(Tea tea){ super(tea); setDescription("红豆"); setPrice(3); } } // 椰果-具体装饰 public class Coconut extends materialDecorator{ public Coconut(Tea tea){ super(tea); setDescription("椰果"); setPrice(5); } } // 测试代码 public static main(String[] args){ // 一杯奶茶 Tea mt = new MilkTea(); System.out.pritln("订单:"+ mt.getDescription()); // 奶茶 System.out.pritln("订单费用:"+ mt.cost()); // 10 // 奶茶加红豆 Tea rb = new RedBean(mt); System.out.pritln("订单:"+ rb.getDescription()); // 奶茶,红豆 System.out.pritln("订单费用:"+ rb.cost()); //13 // 奶茶加红豆 + 椰果 Tea cc = new Coconut(rb); System.out.pritln("订单:"+ cc.getDescription());// 奶茶,红豆,椰果 System.out.pritln("订单费用:"+ cc.cost()); //18 }
九、状态模式--多用户登录态
状态模式(State Pattern):用以解决对象在多种状态转换时,需要对外输出不同行为的问题,状态和行为是一一对应的,状态之间可以相互转换。
当一个对象的内在状态改变时,允许改变其行为,这个对象看起来是改变了其类。
主要角色:
Context 环境角色:用于维护State实例,这个实例定义为当前状态。
State 抽象状态角色:定义一个接口封装与Context 的一个特点接口相关行为。
ConcreteState 具体状态角色:每个子类实现一个与Context 的一个状态相关行为。
会员阶梯:白银、黄金、铂金、砖石。不同会员得到不同对待。
// 对行为的抽象,若代码不需要,可不抽象。 public interface VipContext{ void pays(); } // 白银 public class SilverVipContext implements VipContext{ private double price; public VipContextStatus(double price){ this.price = price * 0.9; } @Overide public void pays(){ System.out.println("白银VIP,享受折扣0.9,消费"+price); } } // 抽象状态角色 public class Card{ VipContext vipContext; public void shopping(){ vipContext.pays(); } public void setVipContext(){ this.status = status; } } // 测试代码 public static void main(String[] agrs){ VipContext context = new SilverVipContext(1000); Card card = new card(); card.setState(status); card.shopping(); }
十、享元模式
享元模式中的“享元”指被共享的单元,通过复用对象节省内存。
运用共享有效的支持大量细粒度的对象。
主要运用:在有大量对象时,有可能会造成内存溢出,把其中共同部分抽象出来,相同业务请求来时,直接返回内存中已有的对象,避免重新创建。
场景:系统中有大量对象;对象消耗大量内存;对象的状态可以外部化;这些对象可按照内蕴状态分为很多组,当把外蕴对象剥离出来,每一组都可用一个对象替代;系统不依赖这些对象身份,这些对象不可分辨。
解决思路:用唯一标识码判断,如果在内存中有,则返回这个唯一标识所标识的对象。
关键代码:用HashMap存储对象。
这些类必须有一个工厂对象加以控制。
原理:复用不可变的享元对象,通过工厂模式,使用Map或List缓存享元对象。
例:象棋游戏,游戏大厅可以开十万房间。每个房间差异只是棋子位置。 // 享元类 @AllArgsConstructor @Getter public class ChessPieceUnit{ private int id; private String text; private Color color; private static enum Color { RED, BLACK; } } // 工厂类 public class ChessPieceUnitFactory{ private static final Map<Integer, ChessPieceUnit> PIECES = new HashMap<>(); static { PIECES.put(1, new ChessPieceUnit(1,"車", ChessPieceUnit.Color.BLACK)); PIECES.put(2, new ChessPieceUnit(2,"马", ChessPieceUnit.Color.BLACK)); ... } public static ChessPieceUnit getChessPiece(int chessPieceId){ return PIECES.get(chessPieceId); } } // 具体棋局中的棋子 @AllArgsConstructor @Data public class NewChessPiece { private ChessPieceUnit chessPieceUnit; private int positionX; private int positionY; } // 棋局 public class NewChessBoard{ private Map<Integer, NewChessPiece> chessPieces = new HashMap<>(); public NewChessBoard(){ init(); } private void init() { chessPieces.put(1, new NewChessPiece( ChessPieceUnitFactory.getChessPiece(1),0,0)); ... } public void move(int chessPieceId, int toPositionX, int toPositionY){ // ..... }; }
小结:
1、单例模式:目的是保证全局唯一,一个类只能创建一个对象。
2、享元模式:为实现对象复用,节省内存。
3、联想缓存:为提高访问效率,目的非复用。
4、使用享元模式,由于工厂类一直保存享元对象的引用,导致享元对象在无任何代码使用时,也不会被GC;使用前需验证享元模式是否能大省内存,是否适得其反。
未完待续,接设计模式理解(二)