Нажмите, чтобы узнать подробностиwww.codercc.com
1. Классы и члены минимизируют доступность
-
вопрос
Наиболее важным фактором, позволяющим отличить хорошо спроектированный модуль от плохо спроектированного, является то, скрывает ли модуль свои внутренние данные и другие детали от других внешних модулей. Хорошо спроектированный модуль скрывает все детали реализации, четко изолирует свой API от своей реализации и взаимодействует между модулями только через их API Итак, каковы принципы проектирования при проектировании классов и членов?
-
решить
Существует несколько основных принципов проектирования классов и членов:
- Сделайте каждый класс или член как можно более недоступным и используйте наименьший возможный уровень доступа, совместимый с соответствующей функциональностью программного обеспечения, которое вы пишете; для членов (полей, методов, вложенных классов или вложенных интерфейсов) существует четыре уровня доступа: 1 .private-приватный доступ в этом классе 2.Default level-доступ на уровне пакета 3.protected уровень доступа-все подклассы этого класса или классы пакетов могут получить доступ 4.public-доступ возможен где угодно;
- Если класс переопределяет метод в родительском классе, уровень доступа в подклассе не должен быть ниже уровня доступа в родительском классе, чтобы дочерний класс мог продолжать использоваться везде, где используется экземпляр родительского класса. Если класс реализует интерфейс, то все методы интерфейса в классе должны быть общедоступными;
- Поля экземпляра никогда не должны быть общедоступными, как неконечное поле экземпляра указывает на изменяемый объект, и если поле экземпляра является общедоступным, то класс, содержащий поле экземпляра, небезопасен для потоков;
-
Суммировать
Вкратце,При разработке классов и членов доступность должна быть как можно ниже., за исключением особого случая общедоступных статических конечных полей,Публичные классы не должны содержать общедоступных полей и обеспечивать неизменяемость объектов, на которые ссылаются общедоступные статические конечные поля.
2. Используйте метод доступа
-
вопрос
Есть такой встречный пример:
class Point { public double x; public double y; }
Класс, подобный приведенному выше, никогда не следует объявлять общедоступным, потому что после объявления общедоступного все данные в классе становятся доступными, и его представление данных не может быть изменено, и он не может накладывать какие-либо ограничения при доступе, не может принимать какие-либо вспомогательные меры, так много проблем, причина в том, что если класс объявлен неправильно, все поле данных может быть выставлено клиенту. Хотя для изменяемых классов его следует заменить классом, который содержит приватные поля и только сеттер-методы:
class Point { private double x; private double y; public Point(double x, double y) { this.x = x; this.y = y; } public double getX() { return x; } public double getY() { return y; } public void setX(double x) { this.x = x; } public void setY(double y) { this.y = y; } }
Итак, как должен быть спроектирован уровень доступа к полям данных в классе?
-
решить
- Если домен данных класса доступен за пределами пакета, в котором он находится, предоставляет методы доступа, что сохраняет гибкость внутреннего представления класса. Если публичный класс выставляет свои поля данных, невозможно изменить внутренний интерфейс данных публичного класса в будущем, потому что поля данных, использующие публичный класс, уже разбросаны по всей системе;
- Существует соглашение для публичных классов,Публичные классы никогда не должны выставлять изменяемые поля.
-
В заключение
Публичные классы никогда не должны выставлять изменяемые поля, иногда необходимо использовать закрытые или закрытые вложенные классы уровня пакета для предоставления полей независимо от того, являются ли поля этого класса изменяемыми или неизменяемыми.
3. Сведите к минимуму изменчивость
-
вопрос
**Неизменяемый класс — это класс, экземпляр которого не может быть изменен, не имеет полей данных, содержащихся в экземпляре, инициализируется при создании экземпляра и не может быть изменен в течение жизненного цикла экземпляра. ** В JAVA существует множество неизменяемых классов, таких как String, базовые типы-оболочки для значений, BigInteger и BigDecimal и т. д. Неизменяемые классы потокобезопасны. Неизменяемость имеет много преимуществ, так каковы же принципы проектирования неизменяемых классов?
-
решить
-
Существует несколько правил проектирования неизменяемых классов:
- Не предоставляйте никаких методов установки, которые изменяют поля данных экземпляра.;
- Убедитесь, что класс не будет расширен: чтобы предотвратить злонамеренное изменение объектов экземпляра подклассами, классы должны быть запрещены для расширения подклассами.можно определить как окончательный; Или сделать все конструкторы класса частными или частными на уровне пакета и добавить общедоступную статическую фабрику для замены общедоступного конструктора;
- Все поля являются окончательными;
- Все домены становятся частными, который не позволяет клиентам получать доступ к изменяемым объектам, на которые ссылается домен, и не позволяет клиентам изменять эти объекты;
- обеспечить взаимоисключающий доступ к любым изменяемым компонентам: если в классе есть поля, указывающие на изменяемые объекты, необходимо убедиться, что клиенты не могут получить ссылки на эти объекты;
-
Пример
Например, конкретная реализация String:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** The offset is the first index of the storage that is used. */ private final int offset; /** The count is the number of characters in the String. */ private final int count; /** Cache the hash code for the string */ private int hash; // Default to 0 .... public String(char value[]) { this.value = Arrays.copyOf(value, value.length); } ... public char[] toCharArray() { // Cannot use Arrays.copyOf because of class initialization order issues char result[] = new char[value.length]; System.arraycopy(value, 0, result, 0, value.length); return result; } ... }
Как показано в приведенном выше коде, можно наблюдать следующие детали дизайна:
- Класс String является окончательным и не может быть унаследован
- Все члены строки задаются как частные переменные
- Нет сеттера для значения
- И установите значение и смещение в окончательные.
- При передаче изменяемого массива value[] сделайте глубокую копию вместо прямого копирования value[] во внутреннюю переменную.
- При получении значения он не возвращает ссылку на объект напрямую, а возвращает копию объекта.
-
-
В заключение
Неизменяемые классы имеют много преимуществ, поэтому в соответствующих сценариях приложений вы можете рассмотреть возможность проектирования классов в неизменяемые классы и следовать принципам проектирования неизменяемых классов.
4. Композиция лучше, чем наследование
-
вопрос
когда прошелПодкласс наследует родительский классЭто не лучший способ повторного использования кода и имеет следующие недостатки: 1.В отличие от вызова метода, наследование нарушает инкапсуляцию.. Подкласс зависит от суперкласса, и если конкретные детали реализации суперкласса изменятся, подкласс изменится соответственно. Если родительский класс специально не предназначен для расширения и имеет хорошую документацию; 2. Проблема «самостоятельного использования» в методе родительского класса вызывает логические ошибки в методе дочернего класса, например подсчет количества элементов, вставленных HashSet с момента его создания, вам нужно переопределить метод add() и метод addAll():
public class TestHashSet<E> extends HashSet<E> { private int count = 0; public TestHashSet(int initCap, float loadFactor) { super(initCap, loadFactor); } @Override public boolean add(E e) { count++; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { count += c.size(); return super.addAll(c); } public int getCount() { return count; } public static void main(String[] args) { TestHashSet<String> hashSet = new TestHashSet<String>(16, 0.75f); hashSet.addAll(Arrays.asList(new String[]{"1","2","3"})); System.out.println(hashSet.getCount()); } }
3 печатается, как и ожидалось, но на самом деле печатается 6. Это связано с тем, что внутренняя реализация метода addAll() вызывает метод add(), поэтому общее количество раз равно 3+3=6. Эта ситуация вызвана «самостоятельным использованием» в методе родительского класса. Итак, как решить проблемы, вызванные наследованием?
-
решить
Для проблем, вызванных наследованием, вы можете использоватьсложныйспособ решения,То есть вместо расширения существующего класса в новый класс добавляется приватное поле, которое ссылается на экземпляр существующего класса. Таким образом, существующий класс становится компонентом нового класса, каждый метод экземпляра в новом классе может вызывать метод экземпляра содержащегося класса и возвращать соответствующий результат, это называется пересылкой..
Перепишите приведенный выше TestHash составным/прямым способом, который состоит из двух частей:Сам новый класс и включенный класс пересылки:
// Wrapper class - uses composition in place of inheritance public class InstrumentedSet<E> extends ForwardingSet<E> { private int addCount = 0; public InstrumentedSet(Set<E> s) { super(s); } @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } } // Reusable forwarding class public class ForwardingSet<E> implements Set<E> { private final Set<E> s; public ForwardingSet(Set<E> s) { this.s = s; } public void clear() { s.clear(); } public boolean contains(Object o) { return s.contains(o); } public boolean isEmpty() { return s.isEmpty(); } public int size() { return s.size(); } public Iterator<E> iterator() { return s.iterator(); } public boolean add(E e) { return s.add(e); } public boolean remove(Object o) { return s.remove(o); } public boolean containsAll(Collection<?> c) { return s.containsAll(c); } public boolean addAll(Collection<? extends E> c) { return s.addAll(c); } public boolean removeAll(Collection<?> c) { return s.removeAll(c); } public boolean retainAll(Collection<?> c) { return s.retainAll(c); } public Object[] toArray() { return s.toArray(); } public <T> T[] toArray(T[] a) { return s.toArray(a); } @Override public boolean equals(Object o) { return s.equals(o); } @Override public int hashCode() { return s.hashCode(); } @Override public String toString() { return s.toString(); } }
В приведенном выше примере построены два класса, одинэто класс-оболочка, используемый для расширения операций, привыклиПересылка классов, которые взаимодействуют с существующими классами, вы можете видеть, что в текущей реализации класс-оболочка больше не расширяет Set напрямую, а расширяет свой класс пересылки, а внутри класса пересылки существующий класс Set существует как одно из его полей данных, а класс пересылки реализует The Set интерфейс создан таким образом, что он включает в себя основные операции существующих классов. Каждое действие пересылки напрямую вызывает соответствующий метод существующего класса и возвращает соответствующий результат. Это сохраняет детали реализации, которые полагаются на Set, вне класса-оболочки. Иногда сочетание составления и переадресации неправильно называют «делегированием». С технической точки зрения это не делегирование, если только объект-оболочка не передает себя обернутому объекту.
-
Когда использовать наследование?
Наследование уместно только тогда, когда подкласс действительно является подтипом суперкласса. Для двух классов A и B класс B должен расширять A только в том случае, если между ними действительно существует отношение «есть-a». Если класс B собирается расширить класс A, следует определить один вопрос: действительно ли B также является A? Если нет уверенности в том, что ответ положительный, то B не должен расширять A. Если ответ отрицательный, обычно B должен содержать частный экземпляр A и предоставлять меньший, более простой API: A не является по своей сути частью B, а просто деталью его реализации (клиенты, использующие API). Нет необходимости знать).
-
-
Суммировать
Короче говоря, наследование — это очень мощное средство, но также и много проблем, потому что ононарушает принцип инкапсуляции.Использование наследования уместно только тогда, когда между подклассом и суперклассом существует отношение подтипа. Даже в этом случае наследование может привести к нестабильности, если подкласс и суперкласс находятся в разных пакетах, а суперкласс не предназначен для наследования. Чтобы избежать этого, вместо наследования можно использовать механизмы композиции и пересылки., особенно когда существует соответствующий интерфейс для реализации класса-оболочки. Классы-оболочки не только более надежны, чем подклассы, но и более функциональны.
5. Правильное использование наследства
-
вопрос
Я объяснил, насколько опасно опрометчиво наследовать класс, который предназначен не для наследования, а для реализации подклассов.Тогда, в реальной разработке, как класс, предназначенный для наследования, может считаться безопасным и надежным?
-
решить
- Для хорошо документированного класса, разработанного специально для наследования, документация класса должна точно описывать влияние переопределения каждого метода.Класс должен иметь документацию для самостоятельного использования методов, которые он может переопределить.. Для каждого общедоступного или защищенного метода или конструктора в его документации должно быть указано, какие переопределяемые методы вызываются методом или конструктором, в каком порядке и как результаты каждого вызова влияют на последующие процессы. В более общем случае класс должен документировать, в каких случаях он будет вызывать переопределяемые методы.
- Афоризмы о программной документации:Хорошая документация по API должна описывать, что делает данный метод, а не как он это делает.. С этой точки зрения, приведенный выше документ нарушает это изречение, что является прискорбным следствием нарушения инкапсуляции наследования, поскольку в приведенном выше документе должно быть ясно указано, что вызов переопределяемого метода приводит к Воздействию. Следовательно, чтобы задокументировать класс, чтобы его можно было безопасно разделить на подклассы, необходимо четко описатьвозможно неопределенные детали реализации.
- Также обратите внимание, что специальная документация, необходимая для наследования, загромождает обычную документацию, которая предназначена для того, чтобы позволить программистам создавать экземпляры класса и вызывать методы класса. И специальный документ, кажется, имеет много смешанного содержания. Чтобы разрешить наследование, классы также должны подчиняться некоторым другим ограничениям. **Конструкторы никогда не должны вызывать переопределяемые методы ни прямо, ни косвенно. ** Это связано с тем, что конструктор суперкласса запускается до конструктора подкласса, поэтому переопределенная версия метода в подклассе будет вызываться до запуска конструктора подкласса. Если переопределенная версия метода зависит от какой-либо инициализации, выполняемой конструктором подкласса, метод не будет работать должным образом;
- Запретить создание подклассов для классов, которые не разработаны и не задокументированы для безопасного создания подклассов.. Есть два способа предотвратить создание подклассов: во-первых, объявить класс final. Во-вторых, объявите все конструкторы как частные или частные на уровне пакета и добавьте несколько статических фабрик для замены конструкторов.
-
В заключение
- Запрет наследования может быть неудобен для некоторых программистов, если конкретный класс не реализует стандартный интерфейс, и если считается необходимым разрешить наследование от такого класса, разумным подходом является обеспечение того, чтобы классНикогда не вызывайте какие-либо из его переопределяемых методов и не указывайте это в документах.. В противном случае создание подклассов запрещено.
- Для проблем с самоиспользованием также можно механически устранить функцию самоиспользования переопределяемых методов в классе без изменения их поведения. Переместите тело кода каждого переопределяемого метода в частный вспомогательный метод. И пусть каждый переопределяемый метод вызывает свой частный вспомогательный метод. Затем замените «каждый вызов переопределяемого метода» на «прямой вызов частного вспомогательного метода переопределяемого метода».
6. Представление политик функциональными объектами
-
вопрос
поддержка некоторых языковУказатели функций, прокси, лямбда-выражения, или поддерживать аналогичный механизм, который позволяет программам сохранять и передавать «возможность вызывать специальные функции». Наиболее часто используемым примером является функция сравнения.Передавая разные стратегии сравнения, будут получены разные результаты сравнения, что также является примером шаблона стратегии. Но Java не предоставляет указатели на функции.
-
решение
Java не предоставляет указатель на функцию, но вы можете использовать ссылку на объект для реализации той же функциональности.. Вызов метода для объекта обычно выполняет некоторую операцию над этим объектом. Однако мы также можем определить объекты, методы которых выполняют операции над другими объектами. если класспросто экспортироватьТакой метод, его экземпляр эквивалентен указателю на метод. Такие экземпляры называются функциональными объектами. Рассмотрим такой класс:
-
class StringLengthComparator { public int compare(String s1, String s2) { return s1.length() - s2.length(); } }
Здесь ссылка на объект StringLengthComparator может рассматриваться как "указатель на функцию", указывающий на внутренний компаратор сравнения объекта, который может быть вызван для любой пары строк. Экземпляр StringLengthComparator используется для сравнения конкретной операции сравнения строк. Стратегия. Для данного конкретного класса стратегий все его экземпляры функционально эквивалентны друг другу, поэтому в соответствии с предыдущими принципами он выполнен какSingletonочень подходит:
class StringLengthComparator { private StringLengthComparator() {} public static final StringLengthComparator INSTANCE = new StringLengthComparator(); public int compare(String s1, String s2) { return s1.length() - s2.length(); } }
Однако у этого метода есть проблема, заключающаяся в том, чтоУказывает тип параметра, чтобы нельзя было передать никакую другую стратегию сравнения.. Вместо этого в этом случае вы должны определить интерфейс Comparator и изменить StringLengthComparator для реализации этого интерфейса. Другими словами, при разработке конкретного класса стратегии вам также необходимо определить интерфейс стратегии:
// Strategy interface public interface Comparator<T> { public int compare(T t1, T t2); }
На этом этапе предыдущие конкретные классы политик объявляются следующим образом:
class StringLengthComparator implements Comparator<String> { ...... }
Таким образом, при передаче объекта определенного класса стратегии вам нужно только установить тип параметра в качестве типа интерфейса (использовать интерфейс в качестве определения типа), и теперь вы можете передавать другие стратегии сравнения. использует объявление анонимного класса:
Arrays.sort(stringArray, new Comparator<String>() { public int compare(String s1, String s2) { return s1.length() - s2.length(); } });
Здесь есть проблема,То есть новый экземпляр создается каждый раз, когда выполняется вызов.. Если он выполняется повторно, вам следует рассмотреть возможность сохранения объекта функции вЧастный статический конечный домени использовать его повторно. Еще одним преимуществом этого является осмысленное объявление объекта функции.
Поскольку интерфейс политики используется в качестве типа для всех конкретных экземпляров политики, нам не нужно делать конкретные классы политики общедоступными, чтобы экспортировать конкретные политики.. Вы можете экспортировать общедоступные статические поля или статические фабричные методы, тип которых является интерфейсом стратегии, а конкретный класс стратегии может быть закрытым вложенным классом основного класса:
class Host { private static class StrlenCmp implements Comparator<String>, Serializable { public int compare(String s1, String s2) { return s1.length() - s2.length(); } } // Returned comparator is serializable public static final Comparator<String> STRING_LENGTH_COMPARATOR = new StrlenCmp(); }
-
-
В заключение
- Основная функция указателя функции — реализация шаблона стратегии., чтобы реализовать этот шаблон в Java, объявитьинтерфейс для представления политикии объявляет класс, реализующий интерфейс для каждой конкретной стратегии;
- Когда конкретная стратегия используется только один раз, для объявления и создания экземпляра конкретной стратегии обычно используется анонимный класс;
- Когда конкретная стратегия предназначена для повторного использования, ее класс обычно реализуется какЧастный статический класс-член, который экспортируется через общедоступное статическое конечное поле или статический фабричный метод, его тип - интерфейс стратегии..
7. Отдавайте предпочтение статическим классам-членам
-
вопрос
Вложенный класс — это класс, определенный внутри другого класса. Целью вложенного класса должно быть предоставление услуг для его включающего класса. Если в будущем вложенный класс может использоваться в другом контексте, он должен быть классом верхнего уровня. Существует четыре типа вложенных классов:Статический класс-член (статический класс-член), нестатический класс-член (нестатический класс-член), анонимный класс (анонимный класс) и локальный класс (локальный класс). За исключением первого, остальные три называются внутренними классами. Итак, в каких ситуациях уместно использовать какой вложенный класс?
-
решение
-
статический класс-член
Статический класс-член — это простейший тип вложенного класса.Лучше думать об этом как об обычном классе, который просто объявлен внутри другого класса., который имеет доступ ко всем членам окружающего класса, в том числе объявленным закрытым. Статический класс-член является статическим членом включающего класса и следует тем же правилам доступности, что и другие статические члены. Если он объявлен закрытым, доступ к нему возможен только внутри окружающего класса.
Обычно статические классы-члены используются как общедоступные.Вспомогательный класс, что имеет смысл только при использовании с внешним классом. Например, перечисление, описывающее различные операции, поддерживаемые калькулятором. Перечисление Operation должно быть общедоступным статическим классом-членом класса Calculator, и тогда клиенты, использующие класс Calculator, могут обращаться к этим операциям с такими именами, как Calculator.Operation.PLUS.
** Обычно закрытый статический класс-член используется для представления компонента объекта, представленного окружающим классом. **Например, рассмотрим экземпляр Map, который связывает ключи со значениями. Внутри многие реализации Map имеют объект Entry, соответствующий каждой паре ключ-значение в карте. Хотя каждая запись связана с картой, методам записи не требуется доступ к карте. Следовательно, использование нестатических элементов для представления записей расточительно: лучшим выбором являются статические классы-члены с приватным оформлением. Если вы случайно пропустите модификатор static в объявлении записи, карта все равно будет работать, но каждая запись будет содержать ссылку на карту, что приводит к потере места и времени.
-
нестатический класс-член
Грамматически говоря,Единственная разница между статическим классом-членом и нестатическим классом-членом заключается в том, что объявление статического класса-члена включает модификатор static. Хотя их синтаксис очень похож, два типа вложенных классов совершенно разные. Каждый экземпляр нестатического класса-члена неявно связан с охватывающим экземпляром охватывающего класса. Внутри метода экземпляра нестатического класса-члена вы можете вызвать метод для окружающего экземпляра или использовать модифицированный this, чтобы получить ссылку на окружающий экземпляр. **Если экземпляр вложенного класса может существовать независимо от экземпляра вмещающего класса, вложенный класс должен быть статическим классом-членом.**При отсутствии вмещающего экземпляра для создания экземпляра нестатического члена класс невозможно.
При создании экземпляра нестатического класса-члена также устанавливается связь между ним и окружающим экземпляром, причем эта связь не может быть изменена в дальнейшем. Обычно это управление устанавливается автоматически, когда конструктор нестатического класса-члена вызывается внутри метода экземпляра окружающего класса. Также можно вручную установить это отношение с помощью выражения enclosingInstance.new MemberClass(args), но оно используется редко.
Обычно нестатические классы-члены используются для определения адаптера., что позволяет рассматривать экземпляр внешнего класса как экземпляр другого несвязанного класса. Например, реализации интерфейса Map часто используют нестатические классы-члены для реализации своих представлений коллекций, которые возвращаются методами keySet, entrySet и Values карты. Точно так же реализации интерфейсов коллекций, таких как Set и List, часто используют нестатические классы-члены для реализации своих итераторов:
public class MySet<E> extends AbstractSet<E> { public Iterator<E> iterator(){ return new MyIterator(); } private class MyIterator implements Iterator<E>{ } }
Статический класс-член против нестатического класса-члена
- Если вы объявляете класс-член, которому не требуется доступ к охватывающему экземпляру, всегда добавляйте модификатор static в его жизнь, делая его классом-членом static, а не нестатическим классом-членом. Если модификатор static опущен, каждый экземпляр будет содержать дополнительную ссылку на окружающий объект. Сохранение этой ссылки требует времени и места и приведет к тому, что окружающий экземпляр будет сохранен, хотя и будет иметь право на сборку мусора, что приведет к утечке памяти.;
- Если нет окружающего экземпляра, вам также необходимо выделить экземпляр внутреннего класса, вы не можете использовать нестатический класс-член, так как экземпляр нестатического класса-члена должен иметь вмещающий экземпляр.
-
анонимный класс
**Анонимный класс не имеет имени класса, он не является членом охватывающего класса, он не объявляется вместе с другими членами, а объявляется и инстанцируется одновременно с его использованием. **Анонимные классы могут появляться в любом месте кода, где разрешены выражения. Анонимный класс имеет охватывающий экземпляр тогда и только тогда, когда он появляется в нестатической среде. Но даже если они появляются в статической среде, статические элементы быть не могут.
Применимость анонимных классов имеет множество ограничений. ** Они не могут быть созданы, за исключением случаев, когда они объявлены, и вы не можете выполнять тесты instanceof. **Вы не можете объявить анонимный класс для реализации нескольких интерфейсов или расширить класс, расширить класс и реализовать интерфейс одновременно. Поскольку анонимные классы появляются в выражениях, они должны быть короткими — около 10 строк или меньше, иначе это повлияет на читабельность программы.
Анонимные классы в основном используются для представления функциональных объектов определенных стратегий, таких как Comparator, определенный в методе Arrays.sort(), а также могут использоваться для создания Runnables при создании Thread.
-
местный класс
Локальные классы являются наименее используемыми из четырех типов вложенных классов. Везде, где могут быть объявлены «локальные переменные», могут быть объявлены локальные классы, и локальные классы подчиняются тем же правилам области видимости. Локальные классы имеют общие свойства с каждым из трех других вложенных классов.Как и классы-члены, локальные классы имеют имена, которые можно использовать повторно. Как и анонимные классы, локальные классы имеют вмещающие экземпляры только в том случае, если они фактически определены в нестатической среде и не могут содержать статические члены. Как и анонимные классы, они должны быть короткими, чтобы не влиять на читабельность..
-
-
Суммировать
Короче говоря, есть четыре разных вложенных класса, каждый со своей целью.
-
Если вложенный класс все еще виден за пределами одного метода или он слишком длинный, класс-член не подходит;
-
Если каждый пример класса-члена требует ссылки на периферийный экземпляр, необходимо сделать юниверс класса-члена, иначе он будет статическим;
-
Предполагая, что метод (например, Array.sort()) вызывается, входной параметр метода должен создать экземпляр, и он уже имеет определенный тип.При создании экземпляра этого класса вы можете создать анонимный класс для создания экземпляра этого класса. Если существующего типа нет, он создается как локальный класс.
-