Если нельзя, но очень хочется, то нужно обязательно и ничего в мире не стоит того, чтобы делать из этого проблему!
столько времени читатели провели на блоге -
сейчас онлайн -
Показаны сообщения с ярлыком зависимость. Показать все сообщения
Показаны сообщения с ярлыком зависимость. Показать все сообщения
пятница, 27 июля 2018 г.
Даже не начинай...
понедельник, 19 марта 2012 г.
Java for fun: Что такое Dependency injection, Inversion of Control и почему это возникло. Часть #6
В прошлый раз обещал, что выложу самописный IoC контейнер. Видимо пора бы это сделать уже - столько времени прошло.
Итак исходники качаем тут.
Начнем как всегда с тестов.
Именно так я и написал код, когда я начал разработку этого примера. Никаких классов контейнера естественно не было, и тест не компилился. Но я создал пустые классы с помощью IDE (Ctrl1-1 в Eclipse или Alt-Enter в Idea).
Вот он код пустой
Так теперь код хоть компилится! :) После того как была создана интерфейсная часть, я взялся за реализацию с помощью TDD.
Вот те тестовые сценарии, которые я один за другим написал и реализовал. Кода очень много :) потому что должен был проверить всевозможные случаи инъекции с разными полями, с одинаковыми полями, с наследниками, с реализациями, с примитивами и так далее. Если хочешь скипнуть - жми сюда.
Текста на самом деле много. А еще я все классы сделал Inner классами хотя в реальной версии они должны быть отдельными public классами иначе ничего не получится. Работа с иннераклассами у меня у туду.
Постепенно появлялась реализация. После несокльких подходов к коду я родил вот эти реализации
Классы достаточно сложновастые, но рефлексия все же. Библиотеку Fest Reflection я не хотел подключать из за лишней jar-dependency. В следующий раз попробую сделаю с ней - за одно попиарю этот классный инструмент для работы с рефлексией.
В будущих планах есть сделать то же но со Spring IoC. Ждите, продолжение следует..
Итак исходники качаем тут.
Начнем как всегда с тестов.
package container;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import container.ioc.*;
import container.flashlight.*;
import container.battery.*;
public class TestBaterry {
private Container container;
@Before
public void initContainer() {
// обрати внимание перед каждым тестом проинитится контейнер, которому мы сообщили, что
// заместь батарейки используй ChinaBattery а вместо фонарика SomeFlashlight
container = new ContainerImpl(
Binder.use(ChinaBattery.class).as(Battery.class),
Binder.use(SomeFlashlight.class).as(Flashlight.class));
}
@Test
public void testDischargeNewBattery() {
// таким нехитрым способом по интерфейсу мы получаем реализацию
// контейнер пройдется по всем полям новосозданного объекта и
// если там найдет знакомые типы то вставит в них реализации,
// в соответствии с настройками, которые мы указали
Flashlight flashlight = container.get(Flashlight.class);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
for (int count = 0; count < 1000; count ++) {
flashlight.swithOff();
flashlight.swithOn();
}
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void testBadBattery() {
Battery battery = new Battery(){
@Override
public boolean getVoltage() {
return false;
}
};
// а вот так мы вдруг можем передумать и переопределить настройки контейнера
container.update(Binder.use(battery).as(Battery.class));
Flashlight flashlight = container.get(Flashlight.class);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void testNoGetPowerIfDoubleSwithOn() {
Flashlight flashlight = container.get(Flashlight.class);
assertFalse(flashlight.isShines());
for (int count = 0; count < 1000; count ++) {
flashlight.swithOn();
}
assertTrue(flashlight.isShines());
}
@Test
public void testNoBatteryNoLight() {
container.remove(Battery.class);
Flashlight flashlight = container.get(Flashlight.class);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void integrationTestGetPowerFormNewChinaBattery() {
Flashlight flashlight = container.get(Flashlight.class);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
}
}Именно так я и написал код, когда я начал разработку этого примера. Никаких классов контейнера естественно не было, и тест не компилился. Но я создал пустые классы с помощью IDE (Ctrl1-1 в Eclipse или Alt-Enter в Idea).
Вот он код пустой
package container.ioc;
public class Binder {
public static Binder use(Class<?> classToCreate) {
return null;
}
public static Binder use(Object object) {
return null;
}
public Binder as(Class<?> interfaceClass) {
return null;
}
}package container.ioc;
public interface Container {
<T> T get(Class<T> interfaceClass);
void remove(Class<?> clazz);
void update(Binder binder);
}package container.ioc;
public class ContainerImpl implements Container {
public ContainerImpl(Binder...binders) {
}
@Override
public <T> T get(Class<T> interfaceClass) {
return null;
}
@Override
public void remove(Class<?> clazz) {
}
@Override
public void update(Binder binder) {
}
}Так теперь код хоть компилится! :) После того как была создана интерфейсная часть, я взялся за реализацию с помощью TDD.
Вот те тестовые сценарии, которые я один за другим написал и реализовал. Кода очень много :) потому что должен был проверить всевозможные случаи инъекции с разными полями, с одинаковыми полями, с наследниками, с реализациями, с примитивами и так далее. Если хочешь скипнуть - жми сюда.
Текста на самом деле много. А еще я все классы сделал Inner классами хотя в реальной версии они должны быть отдельными public классами иначе ничего не получится. Работа с иннераклассами у меня у туду.
package container2.ioc.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import container.ioc.Binder;
import container.ioc.ContainerImpl;
public class IoCTest {
interface Marker1 {
}
class Marker1Impl implements Marker1{
}
interface Marker2 {
}
class Marker2Impl implements Marker2 {
}
interface Marker3 {
}
class Marker3Impl implements Marker3 {
}
interface MainMarker {
}
private <T> T getImpl(Class<T> mainClass) {
return (T)new ContainerImpl(
Binder.use(Marker1Impl.class).as(Marker1.class),
Binder.use(Marker2Impl.class).as(Marker2.class),
Binder.use(Marker3Impl.class).as(Marker3.class),
Binder.use(mainClass).as(MainMarker.class)).get(MainMarker.class);
}
class ClassWithDefaultConstructor implements MainMarker {
Marker1 marker1;
public ClassWithDefaultConstructor() {
}
}
@Test
public void testWithDefaultConstructor() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithDefaultConstructor.class).marker1.getClass());
}
class ClassWithoutAnyConstructors implements MainMarker {
Marker1 marker1;
private ClassWithoutAnyConstructors() {
}
}
@Test
public void testWithoutAnyConstructors() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithoutAnyConstructors.class).marker1.getClass());
}
class ClassWithPrivateField implements MainMarker {
private Marker1 marker1;
public ClassWithPrivateField() {}
public Object getMarker1() {
return marker1;
}
}
@Test
public void testWithPrivateField() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithPrivateField.class).getMarker1().getClass());
}
class ClassWithInjectorConstructor implements MainMarker {
private Marker1 marker1;
public ClassWithInjectorConstructor(Marker1 marker1) {
this.marker1 = marker1;
}
public Marker1 getMarker1() {
return marker1;
}
}
@Test
public void testWithInjectorConstructor() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithInjectorConstructor.class).getMarker1().getClass());
}
class СlassWithInjectorConstructorAndTwoFields implements MainMarker {
private Marker1 marker1;
private Marker2 marker2;
public СlassWithInjectorConstructorAndTwoFields(Marker2 marker2, Marker1 marker1) {
this.marker1 = marker1;
this.marker2 = marker2;
}
public Marker1 getMarker1() {
return marker1;
}
public Marker2 getMarker2() {
return marker2;
}
}
@Test
public void testWithInjectorConstructorAndTwoFieldsCheckField1() {
assertEquals(Marker1Impl.class,
getImpl(СlassWithInjectorConstructorAndTwoFields.class).getMarker1().getClass());
}
@Test
public void testWithInjectorConstructorAndTwoFieldsCheckField2() {
assertEquals(Marker2Impl.class,
getImpl(СlassWithInjectorConstructorAndTwoFields.class).getMarker2().getClass());
}
class СlassWithPrivateInjectorConstructorAndTwoFields implements MainMarker {
private Marker1 marker1;
private Marker2 marker2;
private СlassWithPrivateInjectorConstructorAndTwoFields(Marker2 marker2, Marker1 marker1) {
this.marker1 = marker1;
this.marker2 = marker2;
}
public Marker1 getMarker1() {
return marker1;
}
public Marker2 getMarker2() {
return marker2;
}
}
@Test
public void testWithPrivateInjectorConstructorAndTwoFieldsCheckField1() {
assertEquals(Marker1Impl.class,
getImpl(СlassWithPrivateInjectorConstructorAndTwoFields.class).getMarker1().getClass());
}
@Test
public void testWithPrivateInjectorConstructorAndTwoFieldsCheckField2() {
assertEquals(Marker2Impl.class,
getImpl(СlassWithPrivateInjectorConstructorAndTwoFields.class).getMarker2().getClass());
}
class ClassWithTwoConstructors implements MainMarker {
private Marker1 marker1;
public ClassWithTwoConstructors() {
}
private ClassWithTwoConstructors(Marker1 marker) {
this.marker1 = marker;
}
public Object getMarker1() {
return marker1;
}
}
@Test
public void testWithTwoConstructors() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithTwoConstructors.class).getMarker1().getClass());
}
class ClassWithTwoConstructorsWitnSameParametersCount implements MainMarker {
private Marker1 marker1;
private ClassWithTwoConstructorsWitnSameParametersCount(Marker1 marker) {
this.marker1 = marker;
}
private ClassWithTwoConstructorsWitnSameParametersCount(String string) {
}
public Object getMarker1() {
return marker1;
}
}
@Test
public void testWithTwoConstructorsWithSameParametersCount() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithTwoConstructorsWitnSameParametersCount.class).getMarker1().getClass());
}
class ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor implements MainMarker {
private Marker1 marker1;
private Marker2 marker2;
private boolean costructorCall = false;
private ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor(Marker1 marker) {
this.costructorCall = true;
this.marker1 = marker;
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
public boolean isCostructorCall() {
return costructorCall;
}
}
@Test
public void testWithTwoFieldsAndOnlyOneInjectedViaConstructor1() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor.class).getMarker1().getClass());
}
@Test
public void testWithTwoFieldsAndOnlyOneInjectedViaConstructor2() {
assertEquals(Marker2Impl.class,
getImpl(ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor.class).getMarker2().getClass());
}
@Test
public void testWithTwoFieldsAndOnlyOneInjectedViaConstructor_constructorUsed() {
assertTrue(getImpl(ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor.class).isCostructorCall());
}
class ClassWithTwoFieldsAndOtherOneInjectedViaConstructor implements MainMarker {
private Marker1 marker1;
private Marker2 marker2;
private boolean costructorCall = false;
private ClassWithTwoFieldsAndOtherOneInjectedViaConstructor(Marker2 marker) {
this.costructorCall = true;
this.marker2 = marker;
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
public boolean isCostructorCall() {
return costructorCall;
}
}
@Test
public void testWithTwoFieldsAndOtherOneInjectedViaConstructor_CheckField1() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithTwoFieldsAndOtherOneInjectedViaConstructor.class).getMarker1().getClass());
}
@Test
public void testWithTwoFieldsAndOtherOneInjectedViaConstructor_CheckField2() {
assertEquals(Marker2Impl.class,
getImpl(ClassWithTwoFieldsAndOtherOneInjectedViaConstructor.class).getMarker2().getClass());
}
@Test
public void testWithTwoFieldsAndOtherOneInjectedViaConstructor_constructorUsed() {
assertTrue(getImpl(ClassWithTwoFieldsAndOtherOneInjectedViaConstructor.class).isCostructorCall());
}
class ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor implements MainMarker {
private Marker1 marker1;
private Marker2 marker2;
private boolean costructorCall = false;
private Marker3 marker3;
private ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor(Marker3 marker) {
this.costructorCall = true;
this.marker3 = marker;
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
public boolean isCostructorCall() {
return costructorCall;
}
public Object getMarker3() {
return marker3;
}
}
@Test
public void testWithThreeFieldsAndOtherOneInjectedViaConstructor_CheckField1() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor.class).getMarker1().getClass());
}
@Test
public void testWithThreeFieldsAndOtherOneInjectedViaConstructor_CheckField2() {
assertEquals(Marker2Impl.class,
getImpl(ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor.class).getMarker2().getClass());
}
@Test
public void testWithThreeFieldsAndOtherOneInjectedViaConstructor_CheckField3() {
assertEquals(Marker3Impl.class,
getImpl(ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor.class).getMarker3().getClass());
}
@Test
public void testWithThreeFieldsAndOtherOneInjectedViaConstructor_constructorUsed() {
assertTrue(getImpl(ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor.class).isCostructorCall());
}
class Marker1Fasade implements Marker1 {
private Marker1 marker;
public Marker1Fasade(Marker1 marker) {
this.marker = marker;
}
public Object getMarker() {
return marker;
}
}
class ClassWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade implements MainMarker {
private Marker1 marker1;
private Marker2 marker2;
private ClassWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade(Marker1 marker) {
this.marker1 = new Marker1Fasade(marker);
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
}
@Test
public void testWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade_checkThatNoReflectionInjectionIfConstructorInjected() {
ClassWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade impl =
getImpl(ClassWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade.class);
assertEquals(Marker1Fasade.class, impl.getMarker1().getClass());
assertEquals(Marker1Impl.class, ((Marker1Fasade)impl.getMarker1()).getMarker().getClass());
}
class ClassWithThreeFieldsAndTwoInjectedViaConstructor implements MainMarker {
private Marker3 marker3;
private boolean costructorCall = false;
private Marker1 marker1;
private Marker2 marker2;
private ClassWithThreeFieldsAndTwoInjectedViaConstructor(Marker3 marker3, Marker2 marker2) {
this.costructorCall = true;
this.marker3 = marker3;
this.marker2 = marker2;
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
public boolean isCostructorCall() {
return costructorCall;
}
public Object getMarker3() {
return marker3;
}
}
@Test
public void testWithThreeFieldsAndTwoInjectedViaConstructor_CheckField1() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithThreeFieldsAndTwoInjectedViaConstructor.class).getMarker1().getClass());
}
@Test
public void testWithThreeFieldsAndTwoInjectedViaConstructor_CheckField2() {
assertEquals(Marker2Impl.class,
getImpl(ClassWithThreeFieldsAndTwoInjectedViaConstructor.class).getMarker2().getClass());
}
@Test
public void testWithThreeFieldsAndTwoInjectedViaConstructor_CheckField3() {
assertEquals(Marker3Impl.class,
getImpl(ClassWithThreeFieldsAndTwoInjectedViaConstructor.class).getMarker3().getClass());
}
@Test
public void testWithThreeFieldsAndTwoInjectedViaConstructor_constructorUsed() {
assertTrue(getImpl(ClassWithThreeFieldsAndTwoInjectedViaConstructor.class).isCostructorCall());
}
class Marker3Fasade implements Marker3 {
private Marker3 marker;
public Marker3Fasade(Marker3 marker) {
this.marker = marker;
}
public Object getMarker() {
return marker;
}
}
class ClassWithThreeFieldsAndTwoConstructors implements MainMarker {
private Marker3 marker3;
private boolean isCommonCostructorCall = false;
private Marker1 marker1;
private Marker2 marker2;
private ClassWithThreeFieldsAndTwoConstructors(Marker1 marker1) {
this.marker1 = marker1;
}
private ClassWithThreeFieldsAndTwoConstructors(Marker3 marker3, Marker2 marker2) {
this.isCommonCostructorCall = true;
this.marker3 = new Marker3Fasade(marker3);
this.marker2 = marker2;
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
public boolean isCommonCostructorCall() {
return isCommonCostructorCall;
}
public Object getMarker3() {
return marker3;
}
}
@Test
public void testWithThreeFieldsAndTwoConstructors_CheckField1() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithThreeFieldsAndTwoConstructors.class).getMarker1().getClass());
}
@Test
public void testWithThreeFieldsAndTwoConstructors_CheckField2() {
assertEquals(Marker2Impl.class,
getImpl(ClassWithThreeFieldsAndTwoConstructors.class).getMarker2().getClass());
}
@Test
public void testWithThreeFieldsAndTwoConstructors_CheckField3() {
ClassWithThreeFieldsAndTwoConstructors impl =
getImpl(ClassWithThreeFieldsAndTwoConstructors.class);
assertEquals(Marker3Fasade.class, impl.getMarker3().getClass());
assertEquals(Marker3Impl.class, ((Marker3Fasade)impl.getMarker3()).getMarker().getClass());
}
@Test
public void testWithThreeFieldsAndTwoConstructors_constructorUsed() {
assertTrue(getImpl(ClassWithThreeFieldsAndTwoConstructors.class).isCommonCostructorCall());
}
class ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed implements MainMarker {
private Marker3 marker3;
private boolean isCommonCostructorCall = false;
private Marker1 marker1;
private Marker2 marker2;
private ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed(Marker1 marker1) {
this.marker1 = marker1;
}
private ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed(Marker3 marker3, Marker2 marker2) {
this.isCommonCostructorCall = true;
this.marker3 = new Marker3Fasade(marker3);
this.marker2 = marker2;
}
private ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed(Marker3 marker3, Marker2 marker2, String bla) {
this.marker3 = marker3;
this.marker2 = marker2;
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
public boolean isCommonCostructorCall() {
return isCommonCostructorCall;
}
public Object getMarker3() {
return marker3;
}
}
@Test
public void testWithThreeFieldsAndThreeConstructorsOneIsNotUsed_CheckField1() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed.class).getMarker1().getClass());
}
@Test
public void testWithThreeFieldsAndThreeConstructorsOneIsNotUsed_CheckField2() {
assertEquals(Marker2Impl.class,
getImpl(ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed.class).getMarker2().getClass());
}
@Test
public void testWithThreeFieldsAndThreeConstructorsOneIsNotUsed_CheckField3() {
ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed impl =
getImpl(ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed.class);
assertEquals(Marker3Fasade.class, impl.getMarker3().getClass());
assertEquals(Marker3Impl.class, ((Marker3Fasade)impl.getMarker3()).getMarker().getClass());
}
@Test
public void testWithThreeFieldsAndThreeConstructorsOneIsNotUsed_constructorUsed() {
assertTrue(getImpl(ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed.class).isCommonCostructorCall());
}
}Постепенно появлялась реализация. После несокльких подходов к коду я родил вот эти реализации
package container.ioc;
public class Binder {
Class<?> interfaceClass;
Class<?> classToCreate;
Object object;
public Binder(Class<?> classToCreate) {
this.classToCreate = classToCreate;
}
public Binder(Object object) {
this.object = object;
}
public static Binder use(Class<?> classToCreate) {
return new Binder(classToCreate);
}
public static Binder use(Object object) {
return new Binder(object);
}
public Binder as(Class<?> interfaceClass) {
this.interfaceClass = interfaceClass;
return this;
}
}package container.ioc;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
public class ContainerImpl implements Container {
private Collection<Binder> binders;
public ContainerImpl(Binder...binders) {
this.binders = new LinkedList<Binder>(Arrays.asList(binders));
}
@Override
public <T> T get(Class<T> interfaceClass) {
Binder binder = find(interfaceClass);
if (binder.object != null) {
return (T) binder.object;
}
Class<?> classToCreate = binder.classToCreate;
List<Constructor<?>> constructors = getCostructors(classToCreate);
List<Object> dependencies = getObjectsFor(onlyInterfaceTypes(getTypes(getFields(classToCreate))));
return (T) foundCommonConstructor(constructors, dependencies).newInstanceFor(dependencies);
}
private InstanceMaker foundCommonConstructor(
List<Constructor<?>> constructors, List<Object> dependencies)
{
sortByParameterCount(constructors);
for (Constructor<?> constructor : constructors) {
if (sufficiently(constructor.getParameterTypes(), dependencies)) {
return new InstanceMaker(constructor);
}
}
throw new RuntimeException("Constructor not found");
}
private void sortByParameterCount(List<Constructor<?>> constructors) {
Collections.sort(constructors, new Comparator<Constructor<?>>() {
@Override
public int compare(Constructor<?> constructor1, Constructor<?> constructor2) {
return constructor2.getParameterTypes().length -
constructor1.getParameterTypes().length;
}
});
}
private boolean sufficiently(Class<?>[] parameterTypes, List<Object> dependencies) {
for (Class<?> clazz : parameterTypes) {
if (notIn(clazz, dependencies)) {
return false;
}
}
return true;
}
private boolean notIn(Class<?> clazz, List<Object> dependencies) {
for (Object dependency : dependencies) {
if (clazz.isAssignableFrom(dependency.getClass())) {
return false;
}
}
return true;
}
private List<Class<?>> getTypes(Collection<Field> fields) {
List<Class<?>> result = new LinkedList<Class<?>>();
for (Field field : fields) {
result.add(field.getType());
}
return result;
}
private List<Class<?>> onlyInterfaceTypes(List<Class<?>> classes) {
List<Class<?>> result = new LinkedList<Class<?>>();
for (Class<?> clazz : classes) {
try {
find(clazz);
result.add(clazz);
} catch (RuntimeException e) {
continue;
}
}
return result;
}
private Binder find(Class<?> interfaceClass) {
for (Binder binder : binders) {
if (binder.interfaceClass.equals(interfaceClass)) {
return binder;
}
}
throw new RuntimeException("Dependency class not found for interface" + interfaceClass.getName());
}
private List<Object> getObjectsFor(List<Class<?>> parameterTypes) {
List<Object> result = new LinkedList<Object>();
for (Class<?> clazz : parameterTypes) {
result.add(get(clazz));
}
return result;
}
private List<Constructor<?>> getCostructors(Class<?> classToCreate) {
return Arrays.asList(classToCreate.getDeclaredConstructors());
}
private Collection<Field> getFields(Class<?> classToCreate) {
return Arrays.asList(classToCreate.getDeclaredFields());
}
@Override
public void remove(Class<?> clazz) {
for (Binder binder : binders) {
if (binder.interfaceClass.equals(clazz)) {
binders.remove(binder);
return;
}
}
}
@Override
public void update(Binder binder) {
remove(binder.interfaceClass);
binders.add(binder);
}
}package container.ioc;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import java.util.List;
public class InstanceMaker {
private Constructor<?> constructor;
public InstanceMaker(Constructor<?> constructor) {
this.constructor = constructor;
}
public Object newInstanceFor(List<Object> dependencies) {
List<Object> foundDependencies = getDependenciesFor(constructor, dependencies);
Object object = getObject(constructor, foundDependencies);
dependencies.removeAll(foundDependencies);
return injectToField(object, dependencies);
}
private List<Object> getDependenciesFor(Constructor<?> constructor, List<Object> dependencies) {
List<Object> result = new LinkedList<Object> ();
for (Class<?> parameterType : constructor.getParameterTypes()) {
for (Object dependency : dependencies) {
if (parameterType.isAssignableFrom(dependency.getClass())) {
result.add(dependency);
break;
}
}
}
return result;
}
private Object getObject(Constructor<?> constructor, List<Object> dependencies) {
try {
constructor.setAccessible(true);
Object newInstance = constructor.newInstance(dependencies.toArray(new Object[0]));
constructor.setAccessible(false);
return newInstance;
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private <T> T injectToField(T object, List<Object> dependencies) {
for (Object dependency : dependencies) {
Injector.injectAll(object, dependency);
}
return object;
}
}Классы достаточно сложновастые, но рефлексия все же. Библиотеку Fest Reflection я не хотел подключать из за лишней jar-dependency. В следующий раз попробую сделаю с ней - за одно попиарю этот классный инструмент для работы с рефлексией.
В будущих планах есть сделать то же но со Spring IoC. Ждите, продолжение следует..
пятница, 3 февраля 2012 г.
Почему вредно "смотреть" информацию?
Кто-то когда-то посадил мне в голову мыслю, что телик отупляет. Ну ничего против не имею. От телика отказался и появилось больше времени. Но оставил себе два любимых канала - National Geographic и Discovery. Смотрел их ничего не подозревая. Вот очередная суббота, и я утречком за завтраком включаю какой-то канал из этих двоих и получаю порцию информации. Что происходит дальше? А дальше я иду и делаю обычные дела к которым привык. Утыкаюсь за комп. А там еще порция роликов на фейсбуке про то, как люди прыгают с парашютом, летают в космос, делают какие-то вещи интересные. Посмотрел много всякого. Удовлетворился, так сказать, и дальше смотрю - че там по таскам...
Что случилось? А случилось вот что. Я утром проснулся с желанием познать что-то новое. Не даром меня потянуло на дискавери. И вместо того, чтобы познать это на личном опыте, я доверился тем, кто это уже познал на своем и заснял на видео. Отрезонировал эмоционально на их частоте, удовлетворил свою потребность, и все. Глядя чужой опыт я получил порцию кайфа и больше сегодня ничего не буду делать в этом направлении.
В какой момент я стал это понимать? Когда стал ночевать в другой комнате (там где нет телика). Я как-то сразу перестал смотреть дискавери и нешенал джиографик. И скажу, что соскучился за ними. Но! Теперь я находясь на улице пытаюсь почерпнуть то, что мне давало пассивное просматривание программ и фильмов. Кто-то что-то предложил - а я соглашаюсь, потому что внутри накапливается энергия, которую надо нейтрализовать.
Сегодня ради эксперимента глянул передачу, где что-то там строили масштабное. Кайфонул от увиденного и после этого больше не хочется думать на эту ему.
Теперь я, кажется, стал понимать автора книги "4х часовая рабочая неделя", который сказал - будь голодным к информации.
В общем правило такое буду прививать. Никакой информации, если она реально не нужна мне для того, чтобы сделать следующий шаг.
Думаю то же касается книг и фильмов (а уж тем более сериалов).
"Книги не читать?!! Ты что?!!" - уже слышу мне говорят. Вот так, не читать. Изобретать велосипед. Свой личный велосипед. Часто бывает начинаю читать книгу, с первых сраниц понимаю идею и все - книгу на полку, и дальше сам экспериментировать с мыслью. Свой опыт получать. Потом, через некоторое время (месяц, два, пол года) можно снова открыть книгу и уже поговорить с Автором на равных - ты ему свой опыт, а он тебе свой... Ладно, за книги еще поговорим отдельно...
В эру информации выигрывает тот, кто не потребляет ее. Вот такое вот открытие мне пришло сегодня в душе.
Что случилось? А случилось вот что. Я утром проснулся с желанием познать что-то новое. Не даром меня потянуло на дискавери. И вместо того, чтобы познать это на личном опыте, я доверился тем, кто это уже познал на своем и заснял на видео. Отрезонировал эмоционально на их частоте, удовлетворил свою потребность, и все. Глядя чужой опыт я получил порцию кайфа и больше сегодня ничего не буду делать в этом направлении.
В какой момент я стал это понимать? Когда стал ночевать в другой комнате (там где нет телика). Я как-то сразу перестал смотреть дискавери и нешенал джиографик. И скажу, что соскучился за ними. Но! Теперь я находясь на улице пытаюсь почерпнуть то, что мне давало пассивное просматривание программ и фильмов. Кто-то что-то предложил - а я соглашаюсь, потому что внутри накапливается энергия, которую надо нейтрализовать.
Сегодня ради эксперимента глянул передачу, где что-то там строили масштабное. Кайфонул от увиденного и после этого больше не хочется думать на эту ему.
Теперь я, кажется, стал понимать автора книги "4х часовая рабочая неделя", который сказал - будь голодным к информации.
В общем правило такое буду прививать. Никакой информации, если она реально не нужна мне для того, чтобы сделать следующий шаг.
Думаю то же касается книг и фильмов (а уж тем более сериалов).
"Книги не читать?!! Ты что?!!" - уже слышу мне говорят. Вот так, не читать. Изобретать велосипед. Свой личный велосипед. Часто бывает начинаю читать книгу, с первых сраниц понимаю идею и все - книгу на полку, и дальше сам экспериментировать с мыслью. Свой опыт получать. Потом, через некоторое время (месяц, два, пол года) можно снова открыть книгу и уже поговорить с Автором на равных - ты ему свой опыт, а он тебе свой... Ладно, за книги еще поговорим отдельно...
В эру информации выигрывает тот, кто не потребляет ее. Вот такое вот открытие мне пришло сегодня в душе.
понедельник, 10 октября 2011 г.
Java for fun: Что такое Dependency injection, Inversion of Control и почему это возникло. Часть #5
В прошлом посту мы использовали простую фабрику для создания фонарика с уже вставленными батарейками.
Рассмотрим теперь вариант legаcy code, когда у нас в конструкторе фонарика захардкоджен тип батарейки. Очень неприятный момент потому как классы сцеплены между собой жестко. Усугубим его тем фактом, что менять фонарик нельзя (маленькие рефакторинги допустимы, но как и раньше конструктор фонарика должен в себе содержать вызов конструктора батарейки). Заданием нашим будет проверить этот самый фонарик в отрыве от батарейки которую он упорно инстанциирует.
Вот фонарик.
Как его протестировать со другой батарейкой не добавляя никаких конструкторов, не нарушая инкапсуляцию объекта, изменением модификатора доступа поля battery?
Надо локализировать вызов конструктора ChinaBattery в отдельном методе, который позже переопределить в наследнике. Выглядит это так.
А переопределять будем уже в тесте вот так
Вот так я иногда поступаю, когда в коде нельзя ничего делать ручками, а значит зависимость разорвать не получается, а значит DI через конструктор или сеттер не поможет.
Исходники этого примера можно качнуть тут.
Рассмотрим теперь вариант legаcy code, когда у нас в конструкторе фонарика захардкоджен тип батарейки. Очень неприятный момент потому как классы сцеплены между собой жестко. Усугубим его тем фактом, что менять фонарик нельзя (маленькие рефакторинги допустимы, но как и раньше конструктор фонарика должен в себе содержать вызов конструктора батарейки). Заданием нашим будет проверить этот самый фонарик в отрыве от батарейки которую он упорно инстанциирует.
Вот фонарик.
public class SomeFlashlight implements Flashlight {
private Battery battery;
private boolean swithOn;
public SomeFlashlight() {
this.swithOn = false;
this.battery = new ChinaBattery(); // обратите внимание на эту наглость!
}
@Override
public boolean isShines() {
return (battery != null) && swithOn;
}
@Override
public void swithOn() {
if (!swithOn && battery != null) {
swithOn = battery.getVoltage();
}
}
@Override
public void swithOff() {
swithOn = false;
}
}Как его протестировать со другой батарейкой не добавляя никаких конструкторов, не нарушая инкапсуляцию объекта, изменением модификатора доступа поля battery?
Надо локализировать вызов конструктора ChinaBattery в отдельном методе, который позже переопределить в наследнике. Выглядит это так.
public class SomeFlashlight implements Flashlight {
private Battery battery;
private boolean swithOn;
public SomeFlashlight() {
this.swithOn = false;
this.battery = getBattery(); // вот отсюда ...
}
// ... вот сюда инкапсулировали мы зависимость.
// protected для того, чтобы можно было субклассировать и переопределить этот метод
protected Battery getBattery() {
return new ChinaBattery();
}
...А переопределять будем уже в тесте вот так
public class TestBaterry {
...
// внимание! это поле теста
private Battery battery;
// субклассируем SomeFlashlight и переопределяем в нем getBattery
class MockedSomeFlashlight extends SomeFlashlight {
@Override
protected Battery getBattery() {
return battery;
}
}
@Test
public void testDischargeNewBattery() {
// устанавливаем себе батарейку такую как захотим
this.battery = new DisposableBattery();
// и инстанциируем наш субкласс
MockedSomeFlashlight flashlight = new MockedSomeFlashlight();
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
flashlight.swithOff();
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
...Вот так я иногда поступаю, когда в коде нельзя ничего делать ручками, а значит зависимость разорвать не получается, а значит DI через конструктор или сеттер не поможет.
Исходники этого примера можно качнуть тут.
суббота, 23 июля 2011 г.
Java for fun: Что такое Dependency injection, Inversion of Control и почему это возникло. Часть #4
На этот раз воспользуемся простой фабрикой для создания фонарика с уже вставленными батарейками. Напомню в прошлый раз мы научились использовать setter для инъекции.
Скажу сразу исходники этого примера можно качнуть тут.
Чтобы не дать возможность никому создавать фонарик мы сделаем его конструктор package-защищенным, так же как и сам класс.
Интерфейс фонарика без изменений.
Появился новый класс - фабрика, которая устанавливает в новый фонарик батпрейку.
Конечно, в фонарике используется инъекция через конструктор, как в первом примере - мы лишь вели дополнительную прослойку - фабрику и лишили клиента возможности создавать фонарики собственноручно.
Интерфейс батарейки без изменений.
А вот с созданием батарейки я учудил так же как и с фонариком. Есть фабрика батареек и только она знает, какие батарейки выпускает.
А вот, собственно, и батарейка. Никто не узнает что в фонариках используется китайская батарейка - только батареечная фабрика. О фабрике батареек знает фабрика фонариков, но только о том, что она есть и может выпускать батарейки.
Посмотрим тесты:
Скажу сразу исходники этого примера можно качнуть тут.
Чтобы не дать возможность никому создавать фонарик мы сделаем его конструктор package-защищенным, так же как и сам класс.
package factory.flashlight;
import factory.battery.Battery;
// Класс защищен потому что мы не хотим, чтобы кто-то из другого пакета
// вызывал его. Только фабрика, которая находится в том же пакете.
class SomeFlashlight implements Flashlight {
private Battery battery;
private boolean swithOn;
// Констурктор так же защищен. Береженого Бог бережет.
SomeFlashlight(Battery battery) {
this.swithOn = false;
this.battery = battery;
}
@Override
public boolean isShines() {
return (battery != null) && swithOn;
}
@Override
public void swithOn() {
if (!swithOn && battery != null) {
swithOn = battery.getVoltage();
}
}
@Override
public void swithOff() {
swithOn = false;
}
}Интерфейс фонарика без изменений.
package factory.flashlight;
public interface Flashlight {
void swithOn();
void swithOff();
boolean isShines();
}Появился новый класс - фабрика, которая устанавливает в новый фонарик батпрейку.
package factory.flashlight;
import factory.battery.BatteryFactory;
public class FlashLightFactory {
// простая фабрика знает, какой фонарик и какую батарейку использовать
// но возвращает интерфейс!
public Flashlight getFlashlight() {
return new SomeFlashlight(new BatteryFactory().getBattery());
}
}Конечно, в фонарике используется инъекция через конструктор, как в первом примере - мы лишь вели дополнительную прослойку - фабрику и лишили клиента возможности создавать фонарики собственноручно.
Интерфейс батарейки без изменений.
package factory.battery;
public interface Battery {
boolean getVoltage();
}А вот с созданием батарейки я учудил так же как и с фонариком. Есть фабрика батареек и только она знает, какие батарейки выпускает.
package factory.battery;
public class BatteryFactory {
// Заметь, везде где только можно возвращается интерфейс!
public Battery getBattery() {
return new ChinaBattery();
}
}А вот, собственно, и батарейка. Никто не узнает что в фонариках используется китайская батарейка - только батареечная фабрика. О фабрике батареек знает фабрика фонариков, но только о том, что она есть и может выпускать батарейки.
package factory.battery;
// класс скрыт в пакете
class ChinaBattery implements Battery {
private int power = 5;
@Override
public boolean getVoltage() {
if (power > 0) {
power--;
return true;
}
return false;
}
}Посмотрим тесты:
package factory;
import static org.junit.Assert.*;
import org.junit.Test;
import factory.flashlight.FlashLightFactory;
import factory.flashlight.Flashlight;
public class TestBaterry {
@Test
public void testDischargeNewBattery() {
Flashlight flashlight = new FlashLightFactory().getFlashlight();
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
for (int count = 0; count < 1000; count ++) {
flashlight.swithOff();
flashlight.swithOn();
}
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void testNoGetPowerIfDoubleSwithOn() {
Flashlight flashlight = new FlashLightFactory().getFlashlight();
assertFalse(flashlight.isShines());
for (int count = 0; count < 1000; count ++) {
flashlight.swithOn();
}
assertTrue(flashlight.isShines());
}
@Test
public void integrationTestGetPowerFormNewChinaBattery() {
Flashlight flashlight = new FlashLightFactory().getFlashlight();
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
}
}
Т.к. теперь нет возможности получить фонарик без батарейки и извлечь батарейку, то и тестов поменьше будет. Мы пользуемся фабрикой чтобы получить фонарик, от вызова к вызову фонарик будет с заряженной батарейкой на борту.
Вот и все пока.
Дальше мы попробуем разорвать зависимость, если нам для этого не подходит ни DI через конструктор ни через setter. Читаем тут....
среда, 13 июля 2011 г.
Java for fun: Что такое Dependency injection, Inversion of Control и почему это возникло. Часть #3
Привет!! Вчера я писал про инъекцию с помощью конструктора и, как результат, композицию из зависимого объекта и объекта, его использующего. Но фонарик мы привыкли видеть со съемными батарейками, а потому представляю слегка модифицированную версию вчерашнего кода. Итак инъекция посредством get'тера.
Скажу сразу исходники этого примера можно качнуть тут.
Начнем как всегда с интерфейсов. Описание батарейки - не менялся:
Описание фонарика - добавили метод установки батарейки (setBattery):
Дальше у нас пойдет китайская батарейка - она так же не менялась:
Немного поменялся класс фонарика:
- Конструктор, принимающий батарейку, был удален. Напомню он выглядел так:
- Вместо него был переопределен конструктор по-умолчанию. Нам же надо как-то сказать, что выключатель изначально выключен?
- Так же был добавлен один setter для установки батарейки на место. Метод определен в интерфейсе.
Вот класс целиком и полностью:
А вот и тесты. Они так же поменялись - как минимум был добавлен тест на проверку "а что, если во время работы батарейки нагло извлекли из фонарика?"
Многа букоф :)
Глядя на тесты (как документацию), можно заметить что изначально фонарик не рабочий
А еще если батарейки высунуть, то он перестанет светиться
Во всех остальных случаях, сразу после создания фонарика в него вставляются батарейки
Мораль. Если нам нужна несколько большая гибкость (внимание! тут и ошибок больше можно допустить) то мы вместо композиции используем более общий случай - агрегацию.
Приведем наглядный пример (его я стырил из Википедии). Комната является частью квартиры, следовательно здесь подходит композиция, потому что комната без квартиры существовать не может. А, например, мебель не является неотъемлемой частью квартиры, но в то же время, квартира содержит мебель, поэтому следует использовать агрегацию.
Возвращаясь к фонарикам: обычный фонарик со сменными батарейками - агрегация; светодиодный фонарик, который встроен в зажигалку - композиция.
Напомню, что есть и другие методы инъекции зависимостей, в потому продолжение следует (простая фабрика)...
Скажу сразу исходники этого примера можно качнуть тут.
Начнем как всегда с интерфейсов. Описание батарейки - не менялся:
package getter.battery;
public interface Battery {
boolean getVoltage();
}Описание фонарика - добавили метод установки батарейки (setBattery):
package getter.flashlight;
import getter.battery.Battery;
public interface Flashlight {
void swithOn();
void swithOff();
boolean isShines();
void setBattery(Battery battery);
}Дальше у нас пойдет китайская батарейка - она так же не менялась:
package getter.battery;
public class ChinaBattery implements Battery {
private int power = 5;
@Override
public boolean getVoltage() {
if (power > 0) {
power--;
return true;
}
return false;
}
}Немного поменялся класс фонарика:
- Конструктор, принимающий батарейку, был удален. Напомню он выглядел так:
public SomeFlashlight(Battery battery) {
this.battery = battery;
this.swithOn = false;
}- Вместо него был переопределен конструктор по-умолчанию. Нам же надо как-то сказать, что выключатель изначально выключен?
public SomeFlashlight() {
this.swithOn = false;
}- Так же был добавлен один setter для установки батарейки на место. Метод определен в интерфейсе.
public void setBattery(Battery battery) {
this.battery = battery;
}Вот класс целиком и полностью:
package getter.flashlight;
import getter.battery.Battery;
public class SomeFlashlight implements Flashlight {
private Battery battery;
private boolean swithOn;
public SomeFlashlight() {
this.swithOn = false;
}
public void setBattery(Battery battery) {
this.battery = battery;
}
@Override
public boolean isShines() {
return (battery != null) && swithOn;
}
@Override
public void swithOn() {
if (!swithOn && battery != null) {
swithOn = battery.getVoltage();
}
}
@Override
public void swithOff() {
swithOn = false;
}
}А вот и тесты. Они так же поменялись - как минимум был добавлен тест на проверку "а что, если во время работы батарейки нагло извлекли из фонарика?"
package getter;
import static org.junit.Assert.*;
import getter.battery.Battery;
import getter.battery.ChinaBattery;
import getter.flashlight.Flashlight;
import getter.flashlight.SomeFlashlight;
import org.junit.Test;
public class TestBaterry {
class DisposableBattery implements Battery{
private boolean full = true;
@Override
public boolean getVoltage() {
if (full) {
full = false;
return true;
}
return false;
}
}
@Test
public void testDischargeNewBattery() {
Battery battery = new DisposableBattery();
Flashlight flashlight = new SomeFlashlight();
flashlight.setBattery(battery);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
flashlight.swithOff();
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void testChangeDischargedBattery() {
Battery battery = new DisposableBattery();
Flashlight flashlight = new SomeFlashlight();
flashlight.setBattery(battery);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
flashlight.swithOff();
assertFalse(flashlight.isShines());
flashlight.setBattery(new DisposableBattery());
flashlight.swithOn();
assertTrue(flashlight.isShines());
}
@Test
public void testBadBattery() {
Battery battery = new Battery(){
@Override
public boolean getVoltage() {
return false;
}
};
Flashlight flashlight = new SomeFlashlight();
flashlight.setBattery(battery);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void testNoGetPowerIfDoubleSwithOn() {
Battery battery = new DisposableBattery();
Flashlight flashlight = new SomeFlashlight();
flashlight.setBattery(battery);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
}
@Test
public void testNoBatteryNoLight() {
Flashlight flashlight = new SomeFlashlight();
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void testRemoveBatteryFromFlashlightDurringLightOn() {
Battery battery = new DisposableBattery();
Flashlight flashlight = new SomeFlashlight();
flashlight.setBattery(battery);
flashlight.swithOn();
assertTrue(flashlight.isShines());
flashlight.setBattery(null);
assertFalse(flashlight.isShines());
}
@Test
public void integrationTestGetPowerFormNewChinaBattery() {
Battery battery = new ChinaBattery();
Flashlight flashlight = new SomeFlashlight();
flashlight.setBattery(battery);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
}
}Многа букоф :)
Глядя на тесты (как документацию), можно заметить что изначально фонарик не рабочий
Flashlight flashlight = new SomeFlashlight(); assertFalse(flashlight.isShines());
А еще если батарейки высунуть, то он перестанет светиться
assertTrue(flashlight.isShines()); flashlight.setBattery(null); assertFalse(flashlight.isShines());
Во всех остальных случаях, сразу после создания фонарика в него вставляются батарейки
Battery battery = ... Flashlight flashlight = new SomeFlashlight(); flashlight.setBattery(battery);
Мораль. Если нам нужна несколько большая гибкость (внимание! тут и ошибок больше можно допустить) то мы вместо композиции используем более общий случай - агрегацию.
Приведем наглядный пример (его я стырил из Википедии). Комната является частью квартиры, следовательно здесь подходит композиция, потому что комната без квартиры существовать не может. А, например, мебель не является неотъемлемой частью квартиры, но в то же время, квартира содержит мебель, поэтому следует использовать агрегацию.
Возвращаясь к фонарикам: обычный фонарик со сменными батарейками - агрегация; светодиодный фонарик, который встроен в зажигалку - композиция.
Напомню, что есть и другие методы инъекции зависимостей, в потому продолжение следует (простая фабрика)...
вторник, 12 июля 2011 г.
Java for fun: Что такое Dependency injection, Inversion of Control и почему это возникло. Часть #2
Привет. Уже давненько я писал первую часть поста, видимо пришло время перейти к практической части.
Рассматривать будем 4 случая инъекции:
- инъекция через конструктор
- инъекция через setter
- подготовка объекта с помощью простой фабрики
- подготовка объекта с помощью IoC контейнера написанного собственноручно
Действующие лица:
- какой-то фонарик - class SomeFlashlight
- описание фонарика - integface Flashlight
- описание батарейки - interface Battery
- китайская батарейка - class ChinaBattery
Скажу сразу исходники этого примера можно качнуть тут.
Батарейка - с ней все просто, она дает некоторое напряжение (true) или не дает его (false), если севшая.
По задумке батарейка разряжается от последовательных вызовов ее метода getVoltage, что реализовано в китайской батарейке
Так как она китайская, то садится за 5 раз.
Идем дальше - интерфейс фонарик
Фонарик может включаться (swithOn) и выключаться (swithOff) а светится он или нет, мы узнаем с помощью метода isShines, возвращающего true/false.
Если посмотреть на реализацию, то станет видно, что свет горит (isShines = true) только, если батарейка есть + если она дает заряд. При включении с батарейки снимается заряд, что равносильно ее разрядке, если вспомнить реализацию китайской одноразки.
Вот тест, демонстрирующий работу фонарика
Если посмотреть на каждый тест, то тут что происходит
То есть вставляется через конструктор какая-то батарейка. Внутри фонарика все равно какая это будет батарейка, лишь бы соответствовала интерфейсу
Конструктор фонарика мило подставит любую батарейку (которая, implements Battery) в свое поле. Это позволит в дальнейшем пользоваться из фонарика всеми методами, объявленными в интерфейсе Battery
Вот так
Любая батарейка (то есть любой класс, главное, чтобы implements Battery) может быть использована фонариком. Фонарик при включении (swithOn()) попросит у батарейки энергии (battery.getVoltage()).
Вообще, так это полиморфизм на интерфейсах - результат выполнения battery.getVoltage() будет зависеть от того, что находится в battery.
И это первый вариант инъекции зависимости - через конструктор. Используется она тогда, когда фонарик не может существовать без батарейки. Представь себе такой одноразовый фонарик, в который вмонтирована батарейка и доступа к ней нет. Села батарейка - выкидываешь фонарик.
Это называется композицией, когда части объекта неотъемлемы от него. Вот хлеб с изюмом состоит из муки, воды, соли, изюма - все это разобрать по частям не возможно. Хлеб - это композит. Раз создали используем все вместе.
Если с хлебом это прокатило, то с фонариком это кажется немного нелогичным. Мы привыкли видеть фонарики в которых можно заменять батарейки, как только те сели. Тут нам поможет другой способ инъекции зависимости - с помощью setter.
Но об этом в следующей серии "инъекция через setter"...
Рассматривать будем 4 случая инъекции:
- инъекция через конструктор
- инъекция через setter
- подготовка объекта с помощью простой фабрики
- подготовка объекта с помощью IoC контейнера написанного собственноручно
Действующие лица:
- какой-то фонарик - class SomeFlashlight
- описание фонарика - integface Flashlight
- описание батарейки - interface Battery
- китайская батарейка - class ChinaBattery
Скажу сразу исходники этого примера можно качнуть тут.
Батарейка - с ней все просто, она дает некоторое напряжение (true) или не дает его (false), если севшая.
package constructor.battery;
public interface Battery {
boolean getVoltage();
}По задумке батарейка разряжается от последовательных вызовов ее метода getVoltage, что реализовано в китайской батарейке
package constructor.battery;
public class ChinaBattery implements Battery {
private int power = 5;
@Override
public boolean getVoltage() {
if (power > 0) {
power--;
return true;
}
return false;
}
}Так как она китайская, то садится за 5 раз.
Идем дальше - интерфейс фонарик
package constructor.flashlight;
public interface Flashlight {
void swithOn();
void swithOff();
boolean isShines();
}Фонарик может включаться (swithOn) и выключаться (swithOff) а светится он или нет, мы узнаем с помощью метода isShines, возвращающего true/false.
Если посмотреть на реализацию, то станет видно, что свет горит (isShines = true) только, если батарейка есть + если она дает заряд. При включении с батарейки снимается заряд, что равносильно ее разрядке, если вспомнить реализацию китайской одноразки.
package constructor.flashlight;
import constructor.battery.Battery;
public class SomeFlashlight implements Flashlight {
private Battery battery;
private boolean swithOn;
public SomeFlashlight(Battery battery) {
this.battery = battery;
this.swithOn = false;
}
@Override
public boolean isShines() {
return (battery != null) && swithOn;
}
@Override
public void swithOn() {
if (!swithOn && battery != null) {
swithOn = battery.getVoltage();
}
}
@Override
public void swithOff() {
swithOn = false;
}
}Вот тест, демонстрирующий работу фонарика
package constructor;
import static org.junit.Assert.*;
import constructor.battery.Battery;
import constructor.battery.ChinaBattery;
import constructor.flashlight.Flashlight;
import constructor.flashlight.SomeFlashlight;
import org.junit.Test;
public class TestBaterry {
class DisposableBattery implements Battery{
private boolean full = true;
@Override
public boolean getVoltage() {
if (full) {
full = false;
return true;
}
return false;
}
}
@Test
public void testDischargeNewBattery() {
Battery battery = new DisposableBattery();
Flashlight flashlight = new SomeFlashlight(battery);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
flashlight.swithOff();
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void testBadBattery() {
Battery battery = new Battery(){
@Override
public boolean getVoltage() {
return false;
}
};
Flashlight flashlight = new SomeFlashlight(battery);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void testNoGetPowerIfDoubleSwithOn() {
Battery battery = new DisposableBattery();
Flashlight flashlight = new SomeFlashlight(battery);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
}
@Test
public void testNoBatteryNoLight() {
Flashlight flashlight = new SomeFlashlight(null);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void integrationTestGetPowerFormNewChinaBattery() {
Battery battery = new ChinaBattery();
Flashlight flashlight = new SomeFlashlight(battery);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
}
}Если посмотреть на каждый тест, то тут что происходит
Battery battery = ... Flashlight flashlight = new SomeFlashlight(battery); ...
То есть вставляется через конструктор какая-то батарейка. Внутри фонарика все равно какая это будет батарейка, лишь бы соответствовала интерфейсу
public class SomeFlashlight implements Flashlight {
private Battery battery;
...
public SomeFlashlight(Battery battery) {
this.battery = battery;
...
}
...
Конструктор фонарика мило подставит любую батарейку (которая, implements Battery) в свое поле. Это позволит в дальнейшем пользоваться из фонарика всеми методами, объявленными в интерфейсе Battery
public interface Battery {
boolean getVoltage();
}Вот так
public class SomeFlashlight implements Flashlight {
...
@Override
public void swithOn() {
if (!swithOn && battery != null) {
swithOn = battery.getVoltage();
}
}
...Любая батарейка (то есть любой класс, главное, чтобы implements Battery) может быть использована фонариком. Фонарик при включении (swithOn()) попросит у батарейки энергии (battery.getVoltage()).
Вообще, так это полиморфизм на интерфейсах - результат выполнения battery.getVoltage() будет зависеть от того, что находится в battery.
И это первый вариант инъекции зависимости - через конструктор. Используется она тогда, когда фонарик не может существовать без батарейки. Представь себе такой одноразовый фонарик, в который вмонтирована батарейка и доступа к ней нет. Села батарейка - выкидываешь фонарик.
Это называется композицией, когда части объекта неотъемлемы от него. Вот хлеб с изюмом состоит из муки, воды, соли, изюма - все это разобрать по частям не возможно. Хлеб - это композит. Раз создали используем все вместе.
Если с хлебом это прокатило, то с фонариком это кажется немного нелогичным. Мы привыкли видеть фонарики в которых можно заменять батарейки, как только те сели. Тут нам поможет другой способ инъекции зависимости - с помощью setter.
Но об этом в следующей серии "инъекция через setter"...
пятница, 6 августа 2010 г.
Java for fun: Что такое Dependency injection, Inversion of Control и почему это возникло. Часть #1
Сегодня я общался с чудесным человеком, и имел честь ему рассказывать, как я понимаю эту всю хрень, чаще именуемую как dependency injection. Что такое Dependency injection? Как реализуется Dependency injection в Java? Что такое IoC контейнер (из Spring Framework)? Что такое Java Reflection? Почему шаблон проектирования Простая фабрика называется именно так, а не иначе? И много другого вы узнаете из этого поста. Пишу его, пока свежа память. Читаем дальше...
пятница, 30 июля 2010 г.
Подборка #5
Есть и другие подборки: #1, #2, #3, #4, #5, #6, #7, #8, #9, #10, #11, #12, #13, #14, #15, #16, #17, #18, #19, #20, #21, #22, #23, #24, #25, #26, #27, #28, #29, #30, #31, #32, #33, #34, #35, #36, #37, #38, #39, #40
...утром встал в 5:30. Класс! Не перестаю удивляться насколько это время продуктивно. Книжку почитал, работу поделал, в вещах порядок наделал, помечтал. Кайф! Завтра пробую снова...
...вот пока не набьешь шишку сам, не будешь никого слушать. И говорили же мне - сделай три карточки: одну себе, другую жене и третью на черный день. Вот он черный день настал!!! Читать дальше...
...утром встал в 5:30. Класс! Не перестаю удивляться насколько это время продуктивно. Книжку почитал, работу поделал, в вещах порядок наделал, помечтал. Кайф! Завтра пробую снова...
...вот пока не набьешь шишку сам, не будешь никого слушать. И говорили же мне - сделай три карточки: одну себе, другую жене и третью на черный день. Вот он черный день настал!!! Читать дальше...
вторник, 16 декабря 2008 г.
Почему пьют?
Мне сегодня задали вопрос, который часто я задаю себе на следующее утро. "Почему ты вчера пил?" Я перефразировал чтобы стало понятно "ты хочешь спросить, почему я вчера пил на дне рождении у Лины?"
Алкоголь имеет такое свойство - он взамен на часть твоего здоровья переносит все потенциальные позитивные эмоции и энергию с завтрашнего дня в день сегодняшний, когда собственно и пьется. Вот весь алгоритм работы "праздничного" варианта.
Плохо то, что нужен допинг. Нас научили не использовать внутренний потенциал организма и использовать его естественно, а накачиваться ядом чтобы обрести временно то, что и так есть в каждом из нас. Думаю над этим стоит поработать.
Что касается подхода "топить горе в стакане", так это способ заглушить мысли, которые иначе не дают покоя. Снова же есть более естественные способы это сделать, но наиболее простой здесь и сейчас - пойти в магазин и купить что-то. Проблема не решится, а завтра будет болеть еще и голова.
Есть еще и пивные алкоголики к коим я причисляю и себя. Приследуемая цель - легкий микс "праздничного" варианта и подхода "топить горе в стакане". Забить часть мыслей, чтобы с удовольствием пообщаться с друзьями на интересную тему. Фишка в том, что это неопасно но цепляет хорошо. Пиво становится ритуалом, без которого уже невозможно пообщаться с друзьями. Думаю над этим тоже стоит поработать.
В целом спасибо что в компании есть пару человек, которые вообще без алкоголя обходятся. Думаю с вас, Ребята, мне стоит брать пример.
Алкоголь имеет такое свойство - он взамен на часть твоего здоровья переносит все потенциальные позитивные эмоции и энергию с завтрашнего дня в день сегодняшний, когда собственно и пьется. Вот весь алгоритм работы "праздничного" варианта.
Плохо то, что нужен допинг. Нас научили не использовать внутренний потенциал организма и использовать его естественно, а накачиваться ядом чтобы обрести временно то, что и так есть в каждом из нас. Думаю над этим стоит поработать.
Что касается подхода "топить горе в стакане", так это способ заглушить мысли, которые иначе не дают покоя. Снова же есть более естественные способы это сделать, но наиболее простой здесь и сейчас - пойти в магазин и купить что-то. Проблема не решится, а завтра будет болеть еще и голова.
Есть еще и пивные алкоголики к коим я причисляю и себя. Приследуемая цель - легкий микс "праздничного" варианта и подхода "топить горе в стакане". Забить часть мыслей, чтобы с удовольствием пообщаться с друзьями на интересную тему. Фишка в том, что это неопасно но цепляет хорошо. Пиво становится ритуалом, без которого уже невозможно пообщаться с друзьями. Думаю над этим тоже стоит поработать.
В целом спасибо что в компании есть пару человек, которые вообще без алкоголя обходятся. Думаю с вас, Ребята, мне стоит брать пример.
Подписаться на:
Сообщения (Atom)