Паттерны - это решения в дизайне или архитектуре программного обеспечения, которые зарекомендовали себя на практике при решении часто встречающихся проблем.
Делают решение задачи более структурированным
Помогают избегать подводных камней с которыми встречается разработчик
Проверено временем в разных ситуациях ⇨ весьма успешное
Какие бывают:
Объектно-ориентированного программирования
Функционального программирования
Архитектурные/системные (в ИС)
Специфические области (бд, сеть)
Старые паттерны меняются в реализации (например из-за новых возможностей языков)
Новые подходы в программировании 🡆 Новые паттерны
Название
Проблема которую решает
Пример реализации
Что даст в итоге
- в названиях классов
- в комментариях к коду
- в общении
Проще работать с кодом:
public class Display {
public void draw(Object obj){ /* отображаем */ }
}
public class Configuration {
public static Display DISPLAY = new Display();
}
public class GoodSubProgram {
public void doSomething(){
Display display = Configuration.DISPLAY;
display.draw("Hello world");
}
}
public class BadSubProgram {
public void doSomething(){
Display displayDuplicate = new Display();
displayDuplicate.draw("Hello world");
}
}
public class Display {
private Display(){ /* инициализация*/ }
public static final Display INSTANCE = new Display();
public void draw(Object obj){ /* отображаем */ }
}
public class GoodSubProgram {
public void doSomething(){
Display displaySingleton = Display.INSTANCE;
display.draw("Hello world");
}
}
public class BadSubProgram {
public void doSomething(){
// ! не скомпилируется
Display displayDuplicate = new Display();
displayDuplicate.draw("Hello world");
}
}
public class SubProgram {
public SubProgram(boolean withGui,
int port,
KeyProcessor keyProcessor,
Object someParameter1,
Object someParameter2,
Object someParameter3,
Object someParameter4
){}
}
SubProgram someSubProgram1_1 = new SubProgram(true, 9000, new KeyProcessor(Arrays.asList("w", "s"),
Collections.emptyList()), null, null, null, null);
SubProgram someSubProgram2_1 = new SubProgram(
false,
8000,
new KeyProcessor(Collections.singletonList("e"), Collections.emptyList()),
null,
null,
null,
null
);
SubProgramBuilder builder = new SubProgramBuilder();
SubProgram someSubProgram1_2 = builder
.withGui(true)
.port(9000)
.responseToKey("w")
.responseToKey("a")
.build();
SubProgram someSubProgram2_2 = new SubProgramBuilder()
.port(8000)
.responseToKey("e")
.build();
public class SubProgramBuilder {
private static int defaultPort = 1234;
private boolean withGui = false;
private int port = defaultPort;
private Object someParameter1 = new Object();
private Object someParameter2 = new Object();
private Object someParameter3 = new Object();
private Object someParameter4 = new Object();
private List<String> keyBindings = new ArrayList<>();
private List<String> ignoredKeys = new ArrayList<>();
SubProgramBuilder builder = new SubProgramBuilder();
SubProgram someSubProgram1_2 = builder
.withGui(true)
.port(9000)
.responseToKey("w")
.responseToKey("a")
.build();
SubProgram someSubProgram2_2 = new SubProgramBuilder()
.port(8000)
.responseToKey("e")
.build();
public SubProgramBuilder withGui(boolean isWithGui) {
withGui = isWithGui;
return this;
}
public SubProgramBuilder port(int specifiedPort) {
port = specifiedPort;
return this;
}
public SubProgramBuilder responseToKey(String key) {
keyBindings.add(key);
return this;
}
public SubProgram build() {
return new SubProgram(
withGui,
port,
new KeyProcessor(keyBindings, ignoredKeys),
someParameter1,
someParameter2,
someParameter3,
someParameter4
);
}
// нужно прочитать строки из файла
String encoding = "UTF-8";
try {
String filePath = args[0];
URI fileUri = ClassLoader.getSystemClassLoader().getResource(filePath).toURI();
File fileFromUri = new File(fileUri);
Scanner input = new Scanner(fileFromUri, encoding);
List<String> result = new ArrayList<>();
while (input.hasNext()) {
String nextLine = input.nextLine();
result.add(nextLine);
}
input.close();
} catch (Exception e){
// ...
}
public class FileReadingFacade {
// тут что-то что нужно для работы
Object someHeavyImportantStaff = null;
public FileReadingFacade(String someHeavyImportantStuff) {
this.someHeavyImportantStaff = someHeavyImportantStuff;
}
public List<String> readFileForLines(String pathToFile){
try {
URI fileUri = getClass().getResource(pathToFile).toURI();
File fileFromUri = new File(fileUri);
Scanner input = new Scanner(fileFromUri);
List<String> result = new ArrayList<>();
while (input.hasNext()) {
String nextLine = input.nextLine();
result.add(nextLine);
}
input.close();
return result;
} catch (Exception e){
throw new RuntimeException(e);
}
}
}
FileReadingFacade fileReadingFacade =
new FileReadingFacade(getSomeHeavyStuff());
fileReadingFacade.readFileForLines(args[0]);
// между вызовами может сохранять кэш
// или переиспользовать ресурсы
fileReadingFacade.readFileForLines(args[2]);
fileReadingFacade.readFileForLines(args[4]);
public interface SubProgram {
void run();
}
public class SubProgramA implements SubProgram {
@Override
public void run() {
// делает что-то полезное
}
}
public class SubProgramAWithAuthorization extends SubProgramA {
@Override
public void run() {
checkRights();
super.run();
}
private void checkRights(){};
}
public class SubProgramAWithLogging extends SubProgramA {
@Override
public void run() {
logStart();
super.run();
logEnd();
}
private void logStart(){}
private void logEnd(){}
}
public class SubProgramAWithAuthorizationWithLogging
extends SubProgramAWithAuthorization {
@Override
public void run() {
logStart();
super.run();
logEnd();
}
private void logStart(){}
private void logEnd(){}
}
public class AuthorizationWrapper implements SubProgram {
SubProgram wrappedSubProgram;
public AuthorizationWrapper(SubProgram object) {
this.wrappedSubProgram = object;
}
@Override
public void run() {
checkRights();
wrappedSubProgram.run();
}
private void checkRights(){};
}
public class LoggingWrapper implements SubProgram {
SubProgram wrappedSubProgram;
public LoggingWrapper(SubProgram object) {
this.wrappedSubProgram = object;
}
@Override
public void run() {
logStart();
wrappedSubProgram.run();
logEnd();
}
private void logStart() {}
private void logEnd() {}
}
public class NotificationWrapper implements SubProgram {
SubProgram wrappedSubProgram;
public NotificationWrapper(SubProgram object) {
this.wrappedSubProgram = object;
}
@Override
public void run() {
wrappedSubProgram.run();
sendNotification();
}
private void sendNotification(){};
}
SubProgram subProgram = new SubProgramA();
SubProgram wrappedSubprogram1 = new LoggingWrapper(
new NotificationWrapper(
new AuthorizationWrapper(
subProgram
)
)
);
boolean isAuthorizationNeeded = false;
boolean isLoggingEnabled = true;
SubProgram subProgramDynamic = new SubProgramA();
// динамически
if(isAuthorizationNeeded){
subProgramDynamic = new AuthorizationWrapper(subProgramDynamic);
}
if(isLoggingEnabled){
subProgramDynamic = new LoggingWrapper(subProgramDynamic);
}
subProgramDynamic.run();
это поведенческий паттерн проектирования, который даёт возможность последовательно обходить элементы некоторого множества объектов, не раскрывая внутреннего устройства этих множеств
public interface Iterator<E> {
boolean hasNext();
E next();
}
void printAllElements(Collection<String> coll) {
Iterator<String> i = coll.iterator();
while (i.hasNext()) {
String s = i.next();
System.out.println(s);
}
}
void printAllElements(MyTree<String> tree) {
// алгоритм обход дерева и на каждом элементе вызываем 1 раз нужный нам функционал
System.out.println(element);
}
void printAllElements(MyList<String> list) {
// проходим по списку и вызываем на нем нужный функционал
System.out.println(element);
}
void printAllElements(Collection<String> coll) {
Iterator<String> i = coll.iterator();
while (i.hasNext()) {
String s = i.next();
System.out.println(s);
}
}
void printAllElements(Collection<String> coll) {
for (String s : coll) {
System.out.println(s);
}
}
public static class Editor {
private EventBroker eventBroker;
private File file;
public Editor(EventBroker eventBroker) {
this.eventBroker = eventBroker;
}
public void openFile(String filePath) {
this.file = new File(filePath);
eventBroker.newEvent("open", file);
}
public void saveFile() throws Exception {
// сохранение
eventBroker.newEvent("save", file);
}
}
public static class EventBroker {
Map<String, List<EventListener>> listeners = new HashMap<>();
public EventBroker(String... eventTypes) {
for (String operation : eventTypes) {
this.listeners.put(operation, new ArrayList<>());
}
}
public void subscribe(String eventType, EventListener listener) {
List<EventListener> users = listeners.get(eventType);
users.add(listener);
}
public void unsubscribe(String eventType, EventListener listener) {
List<EventListener> users = listeners.get(eventType);
users.remove(users.indexOf(listener));
}
public void newEvent(String eventType, File file) {
List<EventListener> users = listeners.get(eventType);
for (EventListener listener : users) {
listener.handleEvent(eventType, file);
}
}
}
public interface EventListener {
void handleEvent(String eventType, File file);
}
public static class CriticalFilesChangedListener implements EventListener {
@Override
public void handleEvent(String eventType, File file) {
if(isCritical(file)) { alert(); }
}
private boolean isCritical(File file) { return true; }
private void alert() {}
}
public static class LogFileOperationsListener implements EventListener {
@Override
public void handleEvent(String eventType, File file) {
System.out.println("Someone has performed " + eventType
+ " operation with the following file: " + file.getName());
}
}
EventBroker eventBroker = new EventBroker("open", "save");
Editor editor = new Editor(eventBroker);
LogFileOperationsListener operationLogingListener = new LogFileOperationsListener();
eventBroker.subscribe("open", operationLogingListener);
eventBroker.subscribe("save", operationLogingListener);
eventBroker.subscribe("save", new CriticalFilesChangedListener());
try {
editor.openFile("test.txt");
editor.saveFile();
} catch (Exception e) {
e.printStackTrace();
}
Три самых распространенных методов для обработки коллекций данных.
int processData(List<String> list) {
return list.stream()
.filter(s -> s.endsWith("!"))
.map(s -> countWords(s))
.reduce(0, (s1, s2) -> s1 + s2);
}
public abstract class AbstractTest {
abstract protected void prepare();
abstract protected boolean run();
protected void releaseResources(){};
public void runTest() {
prepare();
long start = System.currentTimeMillis();
boolean isSuccessful = run();
TestHelper.logTime(start);
releaseResources();
}
}
public class ConcreteTest extends AbstractTest {
@Override
protected void prepare() {
// какие-то специальные действия по подготовке теста к запуску
}
@Override
protected boolean run() {
// логика самого теста
}
@Override
protected void releaseResources() {
// логика освобождения ресурсов если надо
}
}