简单工厂模式:定义一个工厂类,这个类可以根据传入的参数不同,而返回不同类型的实例对象。通常这些实例对象有着共同的抽象父类或者实现了相同的接口。
这里相当于将不同类创建对象的功能,都放到了一个工厂类里面,然后通过这个工厂类来创建对象。我们需要做的是传入一个参数,而这个参数对应的对象具体是怎么创建的,这个对象又要求什么参数都不用考虑。
假设这里有一家食品制作工厂,这家工厂可以生产各种口味的事物。当我们去购买食物的时候,我们会直接说一份很甜的食物,一份很辣的食物。而不会去具体的描述这个食物应该怎么被制作,应该加多少水,放多少盐。这不是我们需要考虑的事情,这是工厂应该做的事情。我们只需要说一个名字,然后工厂就给我们对应的食物。
另外,通常在具体的工厂类里面,创建对象的方法是静态的,这样我们可以用类名.方法名的方式来调用。因为这样,简单工厂模式又会被称为静态工厂模式。
现在看看具体的代码是怎么实现的
首先是不同的对象应该有相同的父类或者接口
package test.FactoryDemo;
public interface Food {
public abstract void taste();
}
工厂可以生产的食物——很甜的食物
package test.FactoryDemo;
public class Sweet implements Food{
@Override
public void taste() {
System.out.println("很甜的食物");
}
}
工厂可以生产的食物——很辣的食物
package test.FactoryDemo;
public class Hot implements Food{
@Override
public void taste() {
System.out.println("很辣的食物");
}
}
工厂类
package test.FactoryDemo;
public class foodFactory {
//private修饰构造方法,让外界无法创建工厂类的对象
private foodFactory(){
}
public static Food createFood(String taste){
if("sweet".equals(taste)){
return new Sweet();
}else if("hot".equals(taste)){
return new Hot();
}else{
return null;
}
}
}
工厂生产食物并提供给消费者
package test.FactoryDemo;
public class Demo {
public static void main(String[] args) {
Food sweet = foodFactory.createFood("sweet");
sweet.taste();
Food hot = foodFactory.createFood("hot");
hot.taste();
//另外对于工厂制作不了的情况也要有所考虑
Food salty = foodFactory.createFood("salty");
if(salty!=null){
salty.taste();
}else{
System.out.println("工厂无法制作这类食物");
}
}
}
可以发现,我们只是提供了一个参数,说明一下食物的口味,然后工厂就会自动匹配对应的食物并创建对象。接着我们就可以使用这个对象,至于对象的具体创建过程我们并不关心。
单例模式对一个类做出要求,要求在这个类的内部创建对象,而且只能创建一个对象,同时提供一个访问这个对象的方法。确保这个对象的全局唯一性,其他的所有的类需要使用这个类的对象时,都只能使用这个唯一对象。
如何保证类在内存中只有一个对象:
单例模式的应用场景:
单例模式的两种实现形式:
随着类的加载,将这个类的唯一对象也加载好
这是一个班主任的类,老师可以有很多,但是班主任只有一位。家长需要找班主任了解情况,校长需要找班主任开会,学生需要找班主任请假,但是不可能谁找一次班主任,就创建一个新的班主任来满足要求。班主任显然只有一个人,同一时间也只能满足家长,校长,学生其中的一位的要求。所以班主任类改为单例模式,只有班主任这一个实例。谁要找班主任,就去获取这个班主任对象。
package test.SimpleCase;
public class Teacher {
private static Teacher teacher = new Teacher();
private Teacher(){
}
public static Teacher getTeacher(){
return teacher;
}
}
package test.SimpleCase;
public class TeacherDemo {
public static void main(String[] args) {
Teacher t1 = Teacher.getTeacher();
Teacher t2 = Teacher.getTeacher();
System.out.println(t1);
System.out.println(t2);
System.out.println(t1==t2);
}
}
可以看出来从始到终都只有一个对象。
另外这里如果用 Teacher t = new Teacher()的方式来创建对象的话,会直接报错
饿汉式是随着类的加载创建对象
懒汉式是类加载的时候不会创建对象,而是延迟加载,等到需要这个对象的时候才创建
但是这样延迟加载的话会出现线程安全问题,当多个线程都需要这个对象的时候,有可能会出现创建多个对象的情况,比如说
public static Student getStudent(){
if(s==null){
//步骤一
s = new Student(); //步骤二
}
return s; //步骤三
}
当多个线程用这个方法来获取对象时,可能线程一在步骤二加载对象时,线程三在步骤一进行判断,即判断对象是否为空。此时线程一刚刚加载完对象还没有到步骤三返回对象,导致线程三会判断 s 为空,即这个类的对象还没有被创建,于是线程三就会接着创建一个新的对象。
因此这里需要用到synchronized
班长类
package test.SimpleCase;
public class Student {
private static Student s = null;
private Student(){
}
public synchronized static Student getStudent(){
if(s==null){
s = new Student();
}
return s;
}
}
package test.SimpleCase;
public class StudentDemo {
public static void main(String[] args) {
Student s1 = Student.getStudent();
Student s2 = Student.getStudent();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);
}
}
班长类
package test.SimpleCase;
public class Student {
private static Student s = null;
private Student(){
}
private int age = 12;
public synchronized static Student getStudent(){
if(s==null){
s = new Student();
}
return s;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
public void homeWork(){
System.out.println("The monitor collects the class homework");
}
}
线程一
package test.SimpleCase;
public class ThreadDemo extends Thread {
@Override
public void run(){
Student s = Student.getStudent();
synchronized (s){
s.setAge(15);
System.out.println(Thread.currentThread().getName()+"--"+"正在调用对象");
s.homeWork();
System.out.println(s.getAge());
}
}
}
线程二
package test.SimpleCase;
public class ThreadDemo2 extends Thread {
@Override
public void run(){
Student s = Student.getStudent();
synchronized (s){
s.setAge(18);
System.out.println(Thread.currentThread().getName()+"--"+"正在调用对象");
s.homeWork();
System.out.println(s.getAge());
}
}
}
测试方法
package test.SimpleCase;
public class StudentDemo {
public static void main(String[] args) {
ThreadDemo t1 = new ThreadDemo();
ThreadDemo2 t2 = new ThreadDemo2();
t1.start();
t2.start();
Student s1 = Student.getStudent();
System.out.println("Student's age is "+s1.getAge());
}
}