Эта статья является частью нашего курса Академии под названием « Шаблоны проектирования Java» .
В этом курсе вы изучите огромное количество шаблонов проектирования и увидите, как они реализуются и используются в Java. Вы поймете причины, почему шаблоны так важны, и узнаете, когда и как применять каждый из них. Проверьте это здесь !
Содержание
1. Легкая модель
Объектно-ориентированное программирование сделало программирование простым и интересным. Это облегчает работу программиста, моделируя сущности реального мира в мире программирования. Программист создает класс и создает его экземпляр, создавая его объект. Этот объект моделирует сущность реального мира и объекты внутри приложения координируют друг с другом, чтобы выполнить требуемую работу.
Но иногда слишком много объектов могут замедлить ход событий. Слишком много объектов может потреблять большой объем памяти и может замедлить работу приложения или даже вызвать проблемы с нехваткой памяти. Как хороший программист, нужно отслеживать экземпляры объектов и контролировать создание объектов в приложении. Это особенно верно, когда у нас много похожих объектов и два объекта из пула не имеют больших различий между ними.
Иногда объекты в приложении могут иметь большое сходство и иметь сходный вид (подобный вид здесь означает, что большинство их свойств имеют сходные значения, и только некоторые из них имеют разное значение). Если они также являются тяжелыми объектами для создания, они должны контролироваться разработчиком приложения. В противном случае они могут потреблять большую часть памяти и в конечном итоге замедлять работу всего приложения.
Шаблон Flyweight предназначен для управления созданием таких объектов и предоставляет базовый механизм кэширования. Это позволяет вам создавать один объект для каждого типа (тип здесь отличается от свойства этого объекта), и если вы запрашиваете объект с тем же свойством (уже созданным), он вернет вам тот же объект вместо создания нового один.
Прежде чем углубляться в детали шаблона Flyweight, давайте рассмотрим следующий сценарий: сайт, который позволяет пользователям создавать и выполнять программы в Интернете. Мы обсудим сценарий сейчас, а позже попробуем решить проблему с помощью шаблона Flyweight.
Сайт X-программирования позволяет пользователям создавать и выполнять программы, используя свой любимый язык программирования. Он предоставляет вам множество вариантов языка программирования. Вы выбираете один, пишите программу и запускаете ее, чтобы увидеть результат.
Но теперь сайт начал терять своих пользователей, причина в том, что сайт работает медленно. Пользователи больше не заинтересованы в этом. Сайт очень популярен, и иногда его могут использовать более тысячи программистов. Из-за этого сайт ползает. Но интенсивное использование не является реальной проблемой из-за медлительности сайта. Давайте посмотрим на основное программирование сайта, которое позволяет пользователям запускать и выполнять свою программу, и там будет раскрыта истинная проблема.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
package com.javacodegeeks.patterns.flyweightpattern; public class Code { private String code; public String getCode() { return code; } public void setCode(String code) { this .code = code; } } |
Приведенный выше класс используется для установки кода, выполняемого программистом, для его выполнения. Объект Code
представляет собой легкий простой объект, имеющий code
свойства вместе с его установщиком и получателем.
1
2
3
4
5
6
|
package com.javacodegeeks.patterns.flyweightpattern; public interface Platform { public void execute(Code code); } |
Интерфейс Platform
реализован на конкретной языковой платформе для выполнения кода. У него есть один метод executes
, который принимает объект Code
качестве параметра.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
package com.javacodegeeks.patterns.flyweightpattern; public class JavaPlatform implements Platform { public JavaPlatform(){ System.out.println( "JavaPlatform object created" ); } @Override public void execute(Code code) { System.out.println( "Compiling and executing Java code." ); } } |
Вышеупомянутый класс реализует интерфейс Platform
и обеспечивает реализацию для метода execute
, чтобы выполнить код в Java.
Чтобы выполнить код, создается объект Code
который содержит код и объект Platform
для выполнения кода. Код выглядит так:
1
2
|
Platform platform = new JavaPlatform(); platform.execute(code); |
Теперь предположим, что около 2 000 пользователей в сети выполняют свой код, что приводит к объектам 2k Code
объектам 2k Platform
. Объект Code
является легковесным объектом, и для каждого пользовательского кода также должен быть один объект Code. Но Platform
— это тяжелый объект, который используется для настройки среды исполнения. Создание слишком большого количества объектов Platform
занимает много времени и является сложной задачей. Нам нужно контролировать создание объекта Platform
который можно выполнить с помощью шаблона Flyweight, но перед этим давайте рассмотрим детали шаблона Flyweight.
2. Что такое шаблон Flyweight
Цель шаблона Flyweight — использовать общие объекты для эффективной поддержки большого количества мелкозернистых объектов. Flyweight — это общий объект, который может использоваться одновременно в нескольких контекстах. Flyweight действует как независимый объект в каждом контексте — он неотличим от экземпляра объекта, который не является общим. Flyweights не может делать предположения о контексте, в котором они работают. Ключевой концепцией здесь является различие между внутренним и внешним состоянием. Собственное состояние хранится в навесном весе; он состоит из информации, которая не зависит от контекста flyweight, что делает его доступным для совместного использования. Внешнее состояние зависит от контекста мухи и зависит от него и поэтому не может быть разделено. Клиентские объекты несут ответственность за передачу внешнего состояния к весу, когда это необходимо.
Рассмотрим сценарий приложения, который включает создание большого количества объектов, уникальных только с точки зрения нескольких параметров. Другими словами, эти объекты содержат некоторые внутренние, инвариантные данные, которые являются общими для всех объектов. Эти внутренние данные должны быть созданы и поддерживаться как часть каждого создаваемого объекта. Общее создание и обслуживание большой группы таких объектов может быть очень дорогим с точки зрения использования памяти и производительности. Шаблон Flyweight можно использовать в таких сценариях для разработки более эффективного способа создания объектов.
Вот диаграмма классов для шаблона проектирования Flyweight:
легкий вес
- Объявляет интерфейс, через который мухи могут получать и воздействовать на внешнее состояние.
ConcreteFlyweight
- Реализует интерфейс Flyweight и добавляет хранилище для внутреннего состояния, если оно есть. Объект ConcreteFlyweight должен быть разделяемым. Любое состояние, которое оно хранит, должно быть внутренним; то есть он должен быть независимым от контекста объекта ConcreteFlyweight.
FlyweightFactory
- Создает и управляет легковесными объектами.
- Гарантирует, что мухи распределяются правильно. Когда клиент запрашивает flyweight, объект FlyweightFactory предоставляет существующий экземпляр или создает его, если таковой не существует.
клиент
- Поддерживает ссылку на наименьший вес (ы).
- Вычисляет или сохраняет внешнее состояние весов в полете.
3. Решение проблемы
Чтобы решить вышеупомянутую проблему, мы предоставим класс фабрики платформ, который будет управлять созданием объектов Platform.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package com.javacodegeeks.patterns.flyweightpattern; import java.util.HashMap; import java.util.Map; public final class PlatformFactory { private static Map<String, Platform> map = new HashMap<>(); private PlatformFactory(){ throw new AssertionError( "Cannot instantiate the class" ); } public static synchronized Platform getPlatformInstance(String platformType){ Platform platform = map.get(platformType); if (platform== null ){ switch (platformType){ case "C" : platform = new CPlatform(); break ; case "CPP" : platform = new CPPPlatform(); break ; case "JAVA" : platform = new JavaPlatform(); break ; case "RUBY" : platform = new RubyPlatform(); break ; } map.put(platformType, platform); } return platform; } } |
Приведенный выше класс содержит статическую карту, которая содержит объект String
качестве ключа и объект типа Platform
качестве значения. Мы не хотим создавать экземпляр этого класса, поэтому просто держим его конструктор закрытым и бросаем AssertionError
просто для того, чтобы избежать случайного создания объекта даже внутри класса.
Основным и единственным методом этого класса является метод getPlatformInstance
. Это статический метод, в качестве параметра которого используется platformType
. Этот platformType
используется в качестве ключа на карте, он сначала проверяет карту, существует ли уже объект платформы, имеющий ключ, или нет. Если объект не найден, создается соответствующий объект платформы, он помещается в карту, а затем метод возвращает объект. В следующий раз, когда запрашивается тот же объект типа платформы, возвращается тот же существующий объект, а не новый объект.
Также обратите внимание, что метод getPlatformInstance
synchronized
, чтобы обеспечить безопасность потока при проверке и создании экземпляра объекта. В вышеприведенном примере отсутствует какое-либо внутреннее свойство разделяемого объекта, а только внешнее свойство, являющееся объектом кода, предоставленным клиентским кодом.
Теперь давайте проверим код.
01
02
03
04
05
06
07
08
09
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
|
package com.javacodegeeks.patterns.flyweightpattern; public class TestFlyweight { public static void main(String[] args) { Code code = new Code(); code.setCode( "C Code..." ); Platform platform = PlatformFactory.getPlatformInstance( "C" ); platform.execute(code); System.out.println( "-------------------------------------" ); code = new Code(); code.setCode( "C Code2..." ); platform = PlatformFactory.getPlatformInstance( "C" ); platform.execute(code); System.out.println( "-------------------------------------" ); code = new Code(); code.setCode( "JAVA Code..." ); platform = PlatformFactory.getPlatformInstance( "JAVA" ); platform.execute(code); System.out.println( "-------------------------------------" ); code = new Code(); code.setCode( "JAVA Code2..." ); platform = PlatformFactory.getPlatformInstance( "JAVA" ); platform.execute(code); System.out.println( "-------------------------------------" ); code = new Code(); code.setCode( "RUBY Code..." ); platform = PlatformFactory.getPlatformInstance( "RUBY" ); platform.execute(code); System.out.println( "-------------------------------------" ); code = new Code(); code.setCode( "RUBY Code2..." ); platform = PlatformFactory.getPlatformInstance( "RUBY" ); platform.execute(code); } } |
Приведенный выше код приведет к следующему выводу:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
CPlatform object created Compiling and executing C code. ------------------------------------- Compiling and executing C code. ------------------------------------- JavaPlatform object created Compiling and executing Java code. ------------------------------------- Compiling and executing Java code. ------------------------------------- RubyPlatform object created Compiling and executing Ruby code. ------------------------------------- Compiling and executing Ruby code. |
В приведенном выше классе мы сначала создали объект Code
и установили в него код C. Затем мы попросили PlatformFactory
предоставить платформу C для выполнения кода. Позже мы вызвали метод execute
для возвращаемого объекта, минуя объект Code
.
Мы выполнили ту же процедуру, то есть создали и установили объект Code
, а затем запросили объект платформы, специфичный для кода. Вывод ясно показывает, что объекты платформы создаются только в первый раз, когда они запрашиваются; при следующих попытках возвращается тот же объект.
Другие классы, специфичные для платформы, похожи на класс JavaPlatform
, уже показанный.
01
02
03
04
05
06
07
08
09
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
41
42
43
44
|
package com.javacodegeeks.patterns.flyweightpattern; public class CPlatform implements Platform { public CPlatform(){ System.out.println( "CPlatform object created" ); } @Override public void execute(Code code) { System.out.println( "Compiling and executing C code." ); } } package com.javacodegeeks.patterns.flyweightpattern; public class CPPPlatform implements Platform{ public CPPPlatform(){ System.out.println( "CPPPlatform object created" ); } @Override public void execute(Code code) { System.out.println( "Compiling and executing CPP code." ); } } package com.javacodegeeks.patterns.flyweightpattern; public class RubyPlatform implements Platform{ public RubyPlatform(){ System.out.println( "RubyPlatform object created" ); } @Override public void execute(Code code) { System.out.println( "Compiling and executing Ruby code." ); } } |
Если мы пересмотрим, что 2k пользователей одновременно используют сайт, то получится ровно 2k легкого объекта Code
, и будут созданы только 4 тяжелых объекта платформы. Обратите внимание, что мы говорим о 4 объектах платформы, учитывая, что на каждый язык должен быть как минимум один пользователь. Например, если, скажем, ни один пользователь не кодировал с использованием Ruby, в этом сценарии создаются только 3 объекта платформы.
4. Когда использовать шаблон Flyweight
Эффективность модели Flyweight сильно зависит от того, как и где она используется. Примените шаблон «Вес», если выполняются все следующие условия:
- Приложение использует большое количество объектов.
- Стоимость хранения высока из-за огромного количества предметов.
- Большая часть состояния объекта может быть сделана внешней.
- Многие группы объектов могут быть заменены относительно небольшим количеством общих объектов после удаления внешнего состояния.
- Приложение не зависит от идентичности объекта. Поскольку объекты flyweight могут использоваться совместно, тесты идентичности будут возвращать true для концептуально различных объектов.
5. Легкий вес в JDK
Ниже приведены примеры использования шаблона Flyweight в JDK.
-
java.lang.Integer#valueOf(int)
(также для логических, байтовых, символьных, коротких и длинных)
6. Загрузите исходный код
Это был урок по модели Flyweight. Вы можете скачать исходный код здесь: Flyweight Pattern Project