设计原则:
1、里氏替换原则(Liskov Substitution Principle):
此原则的思想是面对接口的编程中,我们要做到最大化的可扩展性和高内聚低耦合。在设计中,很有可能的问题是,一个功能对象类是无法满足一个项目特定的业务需求。这个时候,如果在接口中引用的是某个功能类的父类对象,我们就可以在子类中继承父类所有功能的同时并且扩展特定需求。在接口引入的地方,如果使用子类插入而不产生问题。这就是里氏替换原则。说到这里,其中的子类功能增加的原则与下列所述的开闭原则是相对应的。所以,这些原则是相互作用的
2、单一原则(Single Responsibility Principle):
此原则的意义在于所有类的设计上,需要对功能进行划分,这样的好处是,形成单一变量,容易进行代码的解耦。同时,功能分类使得每个类的作用是非常明确的。
3、依赖注入原则(Dependence Inversion Principle):
此原则的意义其实也是解耦。依赖其实就是一个映射关系。我们每次在其他功能接口中使用的是这个这个接口的名称,即key值,至于这个key值所对应的value值,并不是外部引用着所要关心的事。这就造成了一种接口之间的无关性的假象。同时,在接口发生变动后,我们只需要对映射的键值对进行修改即可。在Java中所有interface的类,其实都有这种异曲同工之处。我们每次都只需要复写这个接口,但是我们不关心这个接口里,到底需要做什么,当然,我们更希望,在接口中做一些我们双方都约定好了的事情。接口,在编程中其实起到的是一种约束和协议的作用。如果继承了一个接口,就是在完成这个接口在设计之初的一种规范。
同时,在提到依赖注入的同时,更应该联系到依赖反转(控制反转,Inversion of Control),在javaWeb中这是与依赖注入相互配套使用的。控制反转的作用是是两个接口之间原本相互依存的关系变成两者都依赖于第三方容器。这样也是为了解耦。控制反转只是一种概念,在这个前提下,我们要做的就是依赖的注入,每次使用键值对的思想,对类进行绑定,然后我们在使用的时候,只要引用key就可以了,而不用去关心value,并且,这是一个不需要new的过程。平常的时候,每次引用对象,我们都需要new一个对象。这很大程度上是依赖于这个引用者的操作控制。但是,在依赖注入后,就可控制反转,我们直接从键值对中通过key的值来获取一个value的对象。这样产生对象的过程是在容器中产生的,而不关引用者什么事了,这就完成了解耦。
4、接口分离原则(Interface Segregation Principle):
此原则,更像是一种协议上的规范。每次我们进行功能分析的时候,都能大概明白完成一个需求所要经历的步骤为几何。或者两个需求可能完成相似的动作。这时,我们把动作先以接口的方式实现出来,就会让人看的一目了然,这是因为,接口不需要实现具体的方法,把整个框架剥离出来。并且,对接口的修改,更是开闭原则的思想
5、迪米特原则(Law of Demeter):
此原则也是为了解耦,因为,如果在A类中要使用B类的功能动作,我们如果直接在A中new出这么一个B类,就将A和B耦合起来了。而如果我们在第三方类或接口中同时实例化,A和B,然后在A中提供一个插入B的接口,不就达到解耦的效果了吗
6、开闭原则(Open for Extension,Closed for Modification):
一个接口,如果不满足需求,应该通过继承的方式进行修改,而不是在本身的接口上修改
设计模式
1、简单工厂模式:
我个人觉得,简单工厂模式,其实就是一个解耦度不高的选择模式,用来,实现父类对不同子类的调用。不过由于Api的子类个数不定,如果添加了子类的话,就需要修改工厂方法,这是一种不解耦的行为。这时,又可以通过依赖注入的原则进行解耦。就是在工厂类所在的包下构建一个.Properties的文件,在文件中对Api所有子类进行描述,而后在工厂方法中通过流来载入这个内容,根据内容,产生相应的对象
应用场景:一个类的不确定生成哪个子类对象的时候,工厂模式,会根据类型进行选择
public class SimpleFactoryModel { public static Api api; private SimpleFactoryModel() { }; public static Api createApi(int type) { if (type == 1) { api = new Api1(); } else if (type == 2) { api = new Api2(); } else if (type == 3) { api = new Api3(); } return api; } } interface Api { public void create(); } class Api1 implements Api { @Override public void create() { System.out.println("create Api 1"); } } class Api2 implements Api { @Override public void create() { System.out.println("create Api 2"); } } class Api3 implements Api { @Override public void create() { System.out.println("create Api 3"); } }
2、(抽象)工厂模式:
我觉得抽象工厂模式,就是来改进简单工厂模式的。思路是,把create()在抽象工厂中形成一个接口,每次结成这个接口,来实现不同子类的实例化选择
public interface AbstractFactoryModel { public Api create(); } class Api1factory implements AbstractFactoryModel{ @Override public Api create() { Api api = new Api1(); return api; } }
3、单例模式:
懒汉和饿汉的区别有两个:第一是懒汉是线程不安全的,饿汉是线程安全的;第二是懒汉实例化对象的时机是需要用到此对象的时候,饿汉实例化对象的时机是引用类本身的时候。
应用场景:对资源的占有权很在意的情况,如I/O操作,数据库操作,文件读写
public class SingleInstanceModel { /*这里赋空值,是懒汉模式和恶汉模式最大的却别*/ /*懒汉模式是在使用到实例对象的时候才会加载实例化对象,适用于数据量大的对象*/ public static SingleInstanceModel mLazyMan = null; /*饿汉模式是在引用到类本身的时候就会加载实例化对象,适用于数据量小的对象*/ public static SingleInstanceModel mHungryMan = new SingleInstanceModel(); private SingleInstanceModel() { }; /* 通过synchronized这个关键字可推,懒汉模式是一种线程不安全的模式 */ public static synchronized SingleInstanceModel newInstanceLazyMan() { if (mLazyMan == null) { mLazyMan = new SingleInstanceModel(); } return mLazyMan; } /*线程安全*/ public static SingleInstanceModel newInstancehungryMan(){ return mHungryMan; } }
我们很多时候要即需要饿汉模式的效率,还要懒汉模式的延迟加载:
public class LazyAndHungryModelCombine { private LazyAndHungryModelCombine() { };
/*由于对于static关键字修饰的内容,当自身被引用的时候,就会加载进固定内存*/
private static class InstanceHolder { private static LazyAndHungryModelCombine model = new LazyAndHungryModelCombine(); } public static LazyAndHungryModelCombine newInstance() { return InstanceHolder.model; } }
懒汉模式的双重加锁:
public class DoubleLockHungrMan { /* * volatile关键字修饰的变量不能在本地线程中缓存数据, 也就意味这每次都需要在进程的公 * 共内存区域去获取数据, 这是对内存的直接操作 */ private static volatile DoubleLockHungrMan hungrMan = null; private DoubleLockHungrMan() { } public static DoubleLockHungrMan newInstance() { if (hungrMan == null) { synchronized (DoubleLockHungrMan.class) { if (hungrMan == null) { hungrMan = new DoubleLockHungrMan(); } } } return hungrMan; } }
依赖反转的例子:
通过类名,生成对象,平常的是通过类,生成对象
try { DoubleLockHungrMan a=(DoubleLockHungrMan) Class.forName("DoubleLockHungrMan").newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); }
这里附上对Properties的操作:
很多时候,properties文件其实就是一个依赖容器,把一些映射放在这个文件中,如果要改变映射只需要改变这里的内容即可
import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class PropertiesUsing { private Properties mProperties = new Properties(); private InputStream mInput = null; public PropertiesUsing() { try { mInput = PropertiesUsing.class.getResourceAsStream("PropertiesUsing.properties"); mProperties.load(mInput); String str1 = mProperties.getProperty("str1"); String str2 = mProperties.getProperty("str2"); System.out.println(str1+str2); } catch (IOException e) { e.printStackTrace(); } } }
str1=hello str2=world
3、建造者模式:
我觉得建造者模式,是将分工进行了细化;当一个产品是有很多的小部件组合,或者是一个产品需要很多设置的时候,并且不同的设置能产生不同效果的时候,就需要用到建造者模式
建造者分为三部分的工作。
<pre name="code" class="java">import java.util.ArrayList; public class BuilderModel { public BuilderModel() { CarBuilder builder = new CarBuilder(); Director director = new Director(builder); ArrayList<String> sequence = new ArrayList<>(); sequence.add("part2"); sequence.add("part1"); sequence.add("part4"); sequence.add("part3"); director.setSequence(sequence); director.construct(); builder.getProduct(); } } class Director { private ArrayList<String> sequence = new ArrayList<>(); private Builder builder; public Director(CarBuilder builder) { this.builder = builder; } public void construct() { if (sequence == null) { System.out.println("sequence is null"); return; } for (String str : sequence) { if ("part1".equals(str)) { builder.makePart1(); } else if ("part2".equals(str)) { builder.makePart2(); } else if ("part3".equals(str)) { builder.makePart3(); } else if ("part4".equals(str)) { builder.makePart4(); } } } public void setSequence(ArrayList<String> sequence) { this.sequence = sequence; } public ArrayList<String> getSequence() { return sequence; } } interface Builder { public void makePart1(); public void makePart2(); public void makePart3(); public void makePart4(); public Car getProduct(); } class CarBuilder implements Builder { private Car car = new Car(); @Override public void makePart1() { car.setPart1("make part1"); } @Override public void makePart2() { car.setPart2("make part2"); } @Override public void makePart3() { car.setPart3("make part3"); } @Override public void makePart4() { car.setPart4("make part4"); } @Override public Car getProduct() { System.out.println(car.toString()); return car; } } class Car { private String part1; private String part2; private String part3; private String part4; public String getPart1() { return part1; } public void setPart1(String part1) { this.part1 = part1; } public String getPart2() { return part2; } public void setPart2(String part2) { this.part2 = part2; } public String getPart3() { return part3; } public void setPart3(String part3) { this.part3 = part3; } public String getPart4() { return part4; } public void setPart4(String part4) { this.part4 = part4; } public String toString() { return "part1=" + part1 + ",part2=" + part2 + ",part3=" + part3 + ",part4=" + part4; } }
4、原型模式:
浅拷贝:在clone的过程中,仅仅是将引用拷贝了一份,而每个引用指向的内容其实还是跟原来的变量指向的引用一致
深拷贝:与上述过程相反,在clone的过程中,将引用指向的对象一起克隆,在内存中重新开辟一个空间,来存放结构相同的数据结构
待续、、、、、、、、