用一个已经创建的实例作为原型,克隆(clone)该原型对象来创建一个和原型相同或相似的新对象,免去创建对象的复杂过程和细节。原型模式分浅拷贝与深拷贝两种。
浅拷贝实现方式:
1.原型类实现CLoneable接口
2.拷贝对象通过原型的clone方法获得新的实例
深拷贝实现方式:
1.原型类实现CLoneable接口
2.原型类重写clone方法,并在克隆对象同时也克隆响应的引用类型成员变量。
————————————–
以下通过国宝熊猫的克隆来理解浅拷贝与深拷贝:
熊猫类包含两个成员属性:熊猫名字与饲养员。熊猫名字为普通字符串对象,饲养员为引用对象。以下分别通过浅拷贝和深拷贝两种方式克隆熊猫对象:
浅拷贝方式:
package net.mosang.prototype; import java.util.Date; /** * * @author heiry * */ public class Panda implements Cloneable { private String name; private Feeder feeder; //重写clone方法 @Override protected Object clone() throws CloneNotSupportedException { Panda panda = (Panda) super.clone(); return panda; } //setter、getter public String getName() { return name; } public void setName(String name) { this.name = name; } public Feeder getFeeder() { return feeder; } public void setFeeder(Feeder feeder) { this.feeder = feeder; } //构造方法 public Panda(String name, Feeder feeder) { super(); this.name = name; this.feeder = feeder; } }
package net.mosang.prototype; public class Feeder { public String name; public void feed() { System.out.println("feed panda...."); } public Feeder(String name) { super(); this.name = name; } }
package net.mosang.prototype; public class Client { public static void main(String[] args) throws CloneNotSupportedException { Feeder feeder = new Feeder("张大胖"); Panda p1 = new Panda("pangpang",feeder); Panda p2 = (Panda) p1.clone(); System.out.println(p1); System.out.println(p2); feeder.name = "张小胖"; System.out.println("p1----->"+p1+"p1饲养员:----->"+p1.getFeeder().name); System.out.println("p2----->"+p2+"p2饲养员:----->"+p2.getFeeder().name); } }
运行结果:
net.mosang.prototype.Panda@15db9742 net.mosang.prototype.Panda@6d06d69c p1----->net.mosang.prototype.Panda@15db9742p1饲养员:----->张小胖 p2----->net.mosang.prototype.Panda@6d06d69cp2饲养员:----->张小胖
可见浅拷贝方式,虽然获得了一个与原型对象一样的新对象,但是对于引用类型成员变量feeder,拷贝的是地址而不是对象本身,换句话说,两个对象的feeder指向的是同一个Feeder对象。
深拷贝方式:
重点在于重写原型类的clone方法:
package net.mosang.prototype; /** * * @author heiry * */ public class Panda implements Cloneable{ private String name; private Feeder feeder; //重写clone方法,并将引用成员变量feeder同时克隆 @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); Panda panda = (Panda) obj ; panda.feeder = (Feeder)this.feeder.clone(); return panda; } //setter、getter public String getName() { return name; } public void setName(String name) { this.name = name; } public Feeder getFeeder() { return feeder; } public void setFeeder(Feeder feeder) { this.feeder = feeder; } //构造方法 public Panda(String name, Feeder feeder) { super(); this.name = name; this.feeder = feeder; } }
package net.mosang.prototype; public class Feeder implements Cloneable{ public String name; public void feed() { System.out.println("feed panda...."); } public Feeder(String name) { super(); this.name = name; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
package net.mosang.prototype; public class Client { public static void main(String[] args) throws CloneNotSupportedException { Feeder feeder = new Feeder("张大胖"); Panda p1 = new Panda("pangpang",feeder); Panda p2 = (Panda) p1.clone(); System.out.println(p1); System.out.println(p2); feeder.name = "张小胖"; System.out.println("p1----->"+p1+"p1饲养员:----->"+p1.getFeeder().name); System.out.println("p2----->"+p2+"p2饲养员:----->"+p2.getFeeder().name); } }
运行结果:
net.mosang.prototype.Panda@15db9742 net.mosang.prototype.Panda@6d06d69c p1----->net.mosang.prototype.Panda@15db9742p1饲养员:----->张小胖 p2----->net.mosang.prototype.Panda@6d06d69cp2饲养员:----->张大胖
可以看到,深拷贝的引用类型成员变量也是各自独立的。
建造者(Builder)模式的主要模块如下:
1. 建造者接口:负责建造各个组件
2.装配者接口:负责将组件组装成产品
3.产品类:成员变量为各组件对象
4.各个组件类
5. 建造者实现类:实现建造者接口
6.装配者实现类:实现装配者接口
7.客户端调用类
这里以建造超级计算机为例,演示构建者模式原理:
假设计算机由CPU/主板/内存三个部件组成,现在要生产一台陌桑超级计算机,CPU为AMD的CPU,主板为华硕主板,内存为256G容量内存。类关系图如下:
使用建造者模式具体做法:
package net.mosang.builder; /** * 具体产品类 * @author heiry * */ public class Computer { private CPU cpu; private Mainboard mainboard; private Memory memory; public CPU getCpu() { return cpu; } public void setCpu(CPU cpu) { this.cpu = cpu; } public Mainboard getMainboard() { return mainboard; } public void setMainboard(Mainboard mainboard) { this.mainboard = mainboard; } public Memory getMemory() { return memory; } public void setMemory(Memory memory) { this.memory = memory; } }
package net.mosang.builder; /** * 构建子组件接口 * @author heiry * */ public interface ComputerBuilder { public CPU buildCPU(); public Mainboard buildMainboard(); public Memory buildMemory(); }
package net.mosang.builder; /** * 装配者接口 * @author heiry * */ public interface ComputerDirector { public abstract Computer directComputer(); }
package net.mosang.builder; /** * 构建者实现类 * 构建陌桑超级计算机部件 * @author wuah * */ public class MosangSuperComputer implements ComputerBuilder { @Override public CPU buildCPU() { System.out.println("陌桑超算使用的是AMD的CPU"); return new CPU("AMD"); } @Override public Mainboard buildMainboard() { System.out.println("陌桑超算使用的是华硕主板"); return new Mainboard("华硕"); } @Override public Memory buildMemory() { System.out.println("陌桑超算使用的是256G内存"); return new Memory(256); } }
package net.mosang.builder; /** * 装配者实现类:返回具体产品实例 * 陌桑超算由富士康代工装配 * @author heiry * */ public class FoxconnDirector implements ComputerDirector { private ComputerBuilder builder; @Override public Computer directComputer() { CPU cpu = builder.buildCPU(); Mainboard mainboard = builder.buildMainboard(); Memory memory = builder.buildMemory(); Computer c = new Computer(); c.setCpu(cpu); c.setMainboard(mainboard); c.setMemory(memory); return c; } public FoxconnDirector(ComputerBuilder builder) { super(); this.builder = builder; } }
package net.mosang.builder; /** * 具体部件类:CPU * @author heiry * */ public class CPU { private String brands; public String getBrands() { return brands; } public void setBrands(String brands) { this.brands = brands; } public CPU(String brands) { super(); this.brands = brands; } }
package net.mosang.builder; /** * 具体部件类:内存 * @author heiry * */ public class Memory { private int capacity; public Memory(int capacity) { super(); this.capacity = capacity; } public int getCapacity() { return capacity; } public void setCapacity(int capacity) { this.capacity = capacity; } }
package net.mosang.builder; /** * 具体部件类:主板 * @author heiry * */ public class Mainboard { private String producer; public String getProducer() { return producer; } public void setProducer(String producer) { this.producer = producer; } public Mainboard(String producer) { super(); this.producer = producer; } }
package net.mosang.builder; /** * 客户端调用类 * @author heiry * */ public class Client { public static void main(String[] args) { MosangSuperComputer msc = new MosangSuperComputer(); FoxconnDirector fd = new FoxconnDirector(msc); Computer myComputer = fd.directComputer(); System.out.println(myComputer); } }
输出结果
陌桑超算使用的是AMD的CPU 陌桑超算使用的是华硕主板 陌桑超算使用的是256G内存 net.mosang.builder.Computer@15db9742
核心点:
—— 单例类只有一个实例对象;
—— 该单例对象必须由单例类自行创建;
—— 单例类对外提供一个访问该单例的全局访问点;
实现方式:
1.构造方法私有
2.提供类自身定义一个静态私有实例
3.向外提供一个静态的公有方法用于创建或获取该静态私有实例,该方法是对外唯一提供的创建实例途径。
主要分类:
——饿汉式:类一旦加载就创建实例,保证在调用静态方法之前单例已经存在了,特点是线程安全。
——懒汉式:只有在第一次调用的时候才会创建,非线程安全,需要考虑线程同步。
package net.mosang.singleton; /** * 饿汉式单例模式 * @author heiry * */ public class HungrySingLeton { private static HungrySingLeton instance = new HungrySingLeton(); private HungrySingLeton() {//私有构造方法 } public static HungrySingLeton getInstance() { //对外提供唯一的获取实例方法 return instance; } }
package net.mosang.singleton; /** * 懒汉式单例模式 * @author heiry * */ public class LazySingLeton { private static LazySingLeton instance =null; private LazySingLeton() {//私有构造方法 } public static synchronized LazySingLeton getInstance() { //synchronized线程同步,避免并发创建多个对象 if(instance==null) { instance = new LazySingLeton(); } return instance; } }
静态工厂方法只有一个工厂类,且如果要增加产品,则需要修改工厂类代码,违背了开闭原则。工厂方法模式则将工厂类颗粒化,为每一种产品单独设立一个工厂类。
package staticfactory; public interface Computer { public abstract void cal(); }
package net.mosang.factory; public class Mac implements Computer { @Override public void cal() { System.out.println("苹果电脑启动。。。。"); } }
package net.mosang.factory; public class HP implements Computer { @Override public void cal() { System.out.println("惠普电脑启动。。。。"); } }
package net.mosang.factory; public interface ComputerFactoryBasic { public abstract Computer CreateComputer(); }
package net.mosang.factory; public class MacFactory implements ComputerFactoryBasic { @Override public Computer CreateComputer() { return new Mac(); } }
package net.mosang.factory; public class HPFactory implements ComputerFactoryBasic { @Override public Computer CreateComputer() { return new HP(); } }
package net.mosang.factory; public class Client { public static void main(String[] args) { Computer computer = new MacFactory().CreateComputer(); Computer computer2 = new HPFactory().CreateComputer(); computer.cal(); computer2.cal(); } }
输出结果
苹果电脑启动。。。。 惠普电脑启动。。。。
作用:在Think in java 系列书籍以及网上大量的文章都写了其各种各样的好处,我认为言过其实,我们用静态工厂方法,主要原因在于可以根据逻辑需求返回实例对象。可以方便实现工厂模式、单例模式。其它如,有语义方法名、返回其返回类型的任何子类型的对象,方便创建参数化类型实例等等都是无关痛痒的东西。
实现:
将创建实例封装在一个工厂类的静态方法中,静态方法不同的参数返回不同的实例。
示例:
如下所示,电脑工厂类,通过提供静态方法getComputer(String Brand)生产不同品牌的电脑(返回Computer实例对象),客户端只需要调用静态工厂方法传入不同参数即可获得目标对象。
package staticfactory; public interface Computer { public abstract void cal(); }
package staticfactory; public class Mac implements Computer { @Overrid public void cal() { System.out.println("苹果电脑启动。。。。"); } }
package staticfactory; public class HP implements Computer { @Override public void cal() { System.out.println("惠普电脑启动。。。。"); } }
package staticfactory; public class ComputerFactory { public static Computer getComputer(String computerBrand) { switch (computerBrand) { case "Mac": return new Mac(); case "HP": return new HP(); default: return null; } } }
package staticfactory; public class Client { public static void main(String[] args) { Computer computer = ComputerFactory.getComputer("Mac"); Computer computer2 = ComputerFactory.getComputer("HP"); computer.cal(); computer2.cal(); } }
运行结果:
苹果电脑启动。。。。 惠普电脑启动。。。。
一.开闭原则
实现方式:
1. 遵循“抽象约束、封装变化”原则,通过接口或者抽象类为实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。
2.参数类型、引用对象尽量使用接口或者抽象类,而不是实现类。
3.缩小功能颗粒度。功能粒度越小,被复用的可能性就越大,重复利用可能性就越高。
二. 里氏替换原则
简单地说,把父类都替换成它的子类,程序的行为没有变化。只要出现父类的地方,均可用子类替换,而且对整体实现并没有任何影响,提高代码的复用性。
实现方式:
子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
三. 依赖倒置原则
——高层模块不应该依赖低层模块,两者都应该依赖其抽象
——抽象不应该依赖细节
——细节应该依赖抽象
实现方式:
1.每个类尽量提供接口或抽象类,或者两者都具备。
2.变量的声明类型尽量是接口或者是抽象,模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。
3.任何类都不应该从具体类派生。
4.尽量不要重写基类已经写好的方法(符合里氏替换原则)。