设计模式
面向对象设计原则
开闭原则(总纲)
扩展,而非修改代码。open for extension, close for modification
里氏替换原则
不要破坏继承关系。子类不要轻易重写父类方法,而是提供新的方法,以防在替换父类时产生异常。
依赖倒置原则
面向接口编程。依赖抽象而非具体(向宏观依赖)。如:方法定义的参数类型为接口类型,实际参数可以传其子类
单一职责原则
实现类要职责单一,功能拆分,以防依赖这个类需要引入许多无关的东西,以便降低颗粒度提高复用性。
接口隔离原则
接口功能要精简单一,可拆分为多个模块,不要试图用一个接口囊括满足所有需要实现的方法
迪米特法则
降低耦合度。没有直接关联的对象之间不要直接互相调用,而是通过中间类。(例:民众与政府之间通过政府机构产生关联,办事、意见建议等通过机构中间类进行)(缺点:产生过多中间类)
合成复用原则
优先通过组合关系而非继承关系(耦合度太高)实现复用(如:不要用黄苹果红苹果继承苹果类,而是给苹果一个颜色作为成员对象(属性),这样增加绿苹果白苹果等就不用修改代码。)
单例模式
概念
一个项目工程中某个资源只创建1个实例对象;
构造函数私有化;
在本类中创建私有的静态本类对象;
提供静态的方法返回这个静态本类对象,以确保对象唯一;
使用场景
一般实体类对象不会用单例模式,类似 打印机、 等才会使用单例,因为只需要共享一份资源实例就好。
饿汉式(立即加载)
缺点——可能 浪费资源 过早创建本类对象
优点——线程安全 的;高效 的
懒汉式(延迟加载)
常规模式
缺点——线程不安全
优点——避免了类被加载时就创建对象导致的资源浪费
线程安全的懒汉式
在获取实例对象的方法声明中 添加 synchronize 关键字 / 将判断并创建实例对象的代码放入synchronized 代码块(确保线程安全 )
推荐在实例对象前加volatile
volatile:可见的
确保其修饰的对象,每一次被读写都是对其他线程可见的
(其他线程即刻收到通知知晓自己的内存地址已经失效,避免多线程下的线程不安全)
创建型模式-对创建对象封装
含义:负责创建对象,将对象的创建和使用分离;不需要知道怎么创建,得到对象即可。
单例singleton
含义
只提供一个对象
基本结构
私有化的构造函数
公开的newInstance方法,返回本类对象
使用场景
线程池对象的创建、类似scanner、random、inputstream等只需创建一个,可一直复用的对象的创建
注意事项
在多线程中需要注意线程同步问题导致创建出多个对象(使用非空判断进行延迟加载、使用synchronize进行同步机制)
分类
饿汉式
public class HungrySingleton {
private static final String filed1 = "a";
private static final int filed2 = new Random().nextInt(10);
//静态,类加载时就创建了对象
private static HungrySingleton hs = new HungrySingleton();
//构造函数必须私有化
private HungrySingleton() {
}
public static String getFiled1() {
return filed1;
}
public static int getFiled2() {
return filed2;
}
//调用方法的时候直接返回创建好的对象
public static HungrySingleton getSingletonInstance() {
return hs;
}
}
懒汉式+线程同步
public class LazySingleton {
private static final String filed1 = "a";
private static final int filed2 = new Random().nextInt(10);
private static LazySingleton ls = null;
//私有化构造函数
private LazySingleton() {
}
public static String getFiled1() {
return filed1;
}
public static int getFiled2() {
return filed2;
}
//方法增加线程同步的关键字,成为同步方法(锁是this)
public synchronized static LazySingleton getSingletonInstance() {
//非空判断,用于延迟加载,非空才创建,已有则不创建
if(ls == null){
ls= new LazySingleton();
}
return ls;
}
}
原型proto
含义
使用一次new调用构造方法+多次clone方法,获得多个类似的某类对象
基本结构
原型类实现cloneable接口并重写clone方法
创建时先调用构造函数new一个原型类对象,并不断调用clone方法获得多个相同或类似的对象(属性相同,地址值不同)
使用场景
创建的对象之间重叠度较高,直接创建一个原型然后复制,比重新创建效率更高的时候
分类
原型模式
public class student implements Cloneable{
private String name;
private int id = new Random().nextInt(10)+10;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public student(String name) {
super();
this.name = name;
this.id = new Random().nextInt(10)+10;
}
public student clone() {
student c2 = null;
try {
c2=(student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return c2;
}
public void gotoschool() {
System.out.println(this);
System.out.println(this.name + "去上学了,他的学号是:" + this.id);
}
}
//对象地址不同但属性相同
public class Demo {
public static void main(String[] args) {
student s = new student("小李");
student s2 = s.clone();
student s3 = s.clone();
student s4 = s2.clone();
s.gotoschool();
//com.study.clone.student@7852e922
//小李去上学了,他的学号是:17
s2.gotoschool();
//com.study.clone.student@4e25154f
//小李去上学了,他的学号是:17
s3.gotoschool();
//com.study.clone.student@70dea4e
//小李去上学了,他的学号是:17
s4.gotoschool();
//com.study.clone.student@5c647e05
//小李去上学了,他的学号是:17
}
}
带管理器的原型模式(new对象的动作彻底从调用时剥离,转到管理者处)
一个接口本身实现cloneable接口并定义clone()方法,其实现类都重写clone()方法。
创建一个ProtoManager类,其核心对象是一个以类名为键,类对象为值的Map
在该类的构造器中,初始化这个map(将这些实现类的类名和new的对象放入map)
然后提供一个getInstance方法,根据类名从map中获取类对象的clone对象(new出来的只有一个,外部调用getInstance得到的都是clone的对象且都是不同的地址)
public interface SchoolMember extends Cloneable {
void gotoschool();
SchoolMember clone();
}
public class student implements Cloneable,SchoolMember{
private String name;
private int id = new Random().nextInt(10)+10;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public student(String name) {
super();
this.name = name;
this.id = new Random().nextInt(10)+10;
}
public student clone() {
student c2 = null;
try {
c2=(student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return c2;
}
public void gotoschool() {
System.out.println(this);
System.out.println(this.name + "去上学了,他的学号是:" + this.id);
}
}
public class teacher implements Cloneable,SchoolMember{
private String name;
private String subject;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public teacher(String name, String subject) {
super();
this.name = name;
this.subject = subject;
}
public teacher clone() {
teacher c2 = null;
try {
c2=(teacher) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return c2;
}
public void gotoschool() {
System.out.println(this);
System.out.println(this.name + "去教课了,他教的科目是:" + this.subject);
}
}
public class ProtoManager {
private Map<String, SchoolMember>ProtoMap = new HashMap<String, SchoolMember>();
public ProtoManager() {
ProtoMap.put("student", new student("小张"));
ProtoMap.put("teacher", new teacher("老王", "数学"));
}
public SchoolMember getProtoTypeSchoolMember(String key) {
return ProtoMap.get(key).clone();
}
}
//测试结果:得到的对象具有相同属性值而地址值不同
public class Demo {
public static void main(String[] args) {
ProtoManager pm = new ProtoManager();
student s = (student)pm.getProtoTypeSchoolMember("student");
teacher t = (teacher)pm.getProtoTypeSchoolMember("teacher");
student s1 = (student)pm.getProtoTypeSchoolMember("student");
teacher t1 = (teacher)pm.getProtoTypeSchoolMember("teacher");
student s2 = s1.clone();
teacher t2 = t1.clone();
student s3 = s1.clone();
teacher t3 = t1.clone();
s.gotoschool();
//com.study.clone.student@7852e922
//小张去上学了,他的学号是:16
t.gotoschool();
//com.study.clone.teacher@4e25154f
//老王去教课了,他教的科目是:数学
System.out.println("-----------------");
s1.gotoschool();
//com.study.clone.student@70dea4e
//小张去上学了,他的学号是:16
t1.gotoschool();
//com.study.clone.teacher@5c647e05
//老王去教课了,他教的科目是:数学
System.out.println("-----------------");
s2.gotoschool();
//com.study.clone.student@33909752
//小张去上学了,他的学号是:16
t2.gotoschool();
//com.study.clone.teacher@55f96302
//老王去教课了,他教的科目是:数学
System.out.println("-----------------");
s3.gotoschool();
//com.study.clone.student@3d4eac69
//小张去上学了,他的学号是:16
t3.gotoschool();
//com.study.clone.teacher@42a57993
//老王去教课了,他教的科目是:数学
}
}
工厂方法FactoryMethod
含义
将产品的创建放在具体工厂类,而无需在调用者中new,也不关心创建细节,实现创建与使用分离
基本结构
产品类,及多种产品类的共同抽象接口
工厂类,及多种工厂类的共同抽象接口
配置文件,及其 读取类(返回一个配置文件所定义的工厂对象)
使用场景
多种同类对象,分别有不同的工厂,需要动态获取不同类的对象
同时不关心细节,只要拿到对象实例
注意事项
复杂度较高,每增加一类产品要增加一个工厂类
如果不使用抽象工厂统一所有工厂,而是使用一个工厂生产所有类,则就是 简单工厂模式
//抽象产品
public interface Animal {
void run();
}
//具体产品
public class Horse implements Animal{
@Override
public void run() {
System.out.println("Horse Run !");
}
public Horse() {
super();
System.out.println("新马出生");
}
}
public class Pig implements Animal{
@Override
public void run() {
System.out.println("Pig Run !");
}
public Pig() {
super();
System.out.println("新猪出生");
}
}
public class Sheep implements Animal{
@Override
public void run() {
System.out.println("Sheep Run !");
}
public Sheep() {
super();
System.out.println("新羊出生");
}
}
//抽象工厂
public interface AbstractFactory {
Animal newInstance();
}
//具体工厂
public class HorseFactory implements AbstractFactory{
public HorseFactory() {
super();
System.out.println("养马场营业");
}
public Animal newInstance(){
return new Horse();
}
}
public class PigFactory implements AbstractFactory{
public PigFactory() {
super();
System.out.println("养猪场营业");
}
public Animal newInstance(){
return new Pig();
}
}
public class SheepFactory implements AbstractFactory{
public SheepFactory() {
super();
System.out.println("牧羊村营业");
}
public Animal newInstance(){
return new Sheep();
}
}
//prperties文件(只需更改此处指定的工厂,即可获得不同的产品)
className=com.study.factory.HorseFactory
//读取配置文件类
public class ReadConfig {
public static Object ReadConfig(){
Properties properties = new Properties();
Object o = null;
InputStream i = new ReadConfig().getClass().getClassLoader().getResourceAsStream("animal.properties");
try {
properties.load(i);
String className =(String) properties.get("className");
Class c = Class.forName(className);
o = c.newInstance();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return o;
}
}
//测试类
public class Demo {
public static void main(String[] args) {
AbstractFactory o =(AbstractFactory) ReadConfig.ReadConfig();
Animal a = o.newInstance();
a.run();
}
}
//养马场营业
//新马出生
//Horse Run !
抽象工厂AbstractFactory
含义
工厂方法模式的拓展升级
基本结构
产品类,及多种产品类的共同抽象接口
工厂类,及多种工厂类的共同抽象接口
配置文件,及其 读取类(返回一个配置文件所定义的工厂对象)
与工厂方法的区别是,一次生产多种同族(同属于一个接口下的)产品
使用场景
一次只使用一个产品族的产品的场景(因为根据配置文件指定创建的产品族)(但可能使用多种)
注意事项
增加产品类下的产品种类时将不满足开闭原则(需要修改代码且每个产品类都要改)
增加产品族时满足开闭原则(增加一个产品类及其工厂类即可)
public interface Product {
public void showName();
public void growUp();
}
public class Animal implements Product {
@Override
public void showName() {
System.out.println("我是小动物");
}
@Override
public void growUp() {
System.out.println("小动物长大了");
}
public Animal() {
super();
System.out.println("小动物出生了");
}
}
public class Plant implements Product {
@Override
public void showName() {
System.out.println("我是植物");
}
@Override
public void growUp() {
System.out.println("植物生长了");
}
public Plant() {
super();
System.out.println("植物发芽了");
}
}
public interface AbstractFarm {
Plant newPlant();
Animal newAnimal();
}
public class MyFarm implements AbstractFarm{
public MyFarm() {
super();
System.out.println("我的农场建成");
}
@Override
public Plant newPlant() {
return new Plant();
}
@Override
public Animal newAnimal() {
return new Animal();
}
}
public class YourFarm implements AbstractFarm{
public YourFarm() {
super();
System.out.println("你的农场建成");
}
@Override
public Plant newPlant() {
return new Plant();
}
@Override
public Animal newAnimal() {
return new Animal();
}
}
public class Demo {
public static void main(String[] args) {
AbstractFarm o =(AbstractFarm) ReadConfig.ReadConfig();
Plant a = o.newPlant();
Animal b = o.newAnimal();
a.showName();
b.growUp();
}
}
//我的农场建成
//植物发芽了
//小动物出生了
//我是植物
//小动物长大了
建造者Builder
含义
对象由多个部件构成,比较复杂。需要组装部件来创建。
使用场景
各部分较稳定,但组合方式变化较大(属性设置频繁且较多)
优点
其中提供一系列set方法,可以使用方法链形式(new XXXBuilder.setA().setB().setC()…),设置属性较为优雅~