Запах кода 308 - Ключ к более безопасному, чистому, более полиморфическому коду

Запах кода 308 - Ключ к более безопасному, чистому, более полиморфическому коду

18 августа 2025 г.

Когда ваши методы возвращают общие типы, вы нарушаете цепочку вызовов

TL; DR: избегайте методов, которые возвращают объект, любой или нулевой вместо конкретных типов. Сделать их полностью полиморфными

Проблемы 😔

  • Пропущенный полиморфизм
  • Плотная связь
  • ИзлишнийНулевые проверки
  • Сбивает с толку возврат
  • Хрупкий код
  • Трудно проверить
  • Безопасность потерянного типа
  • Загрязнение IFS
  • Сломанный полиморфизм
  • Ошибки времени выполнения
  • Неясные контракты
  • Тестирование трудностей
  • Плохая обслуживаемость

Решения 😃

  1. ВозвращатьсяПолиморфные типы
  2. ИспользоватьОбразец нулевого объекта
  3. Избегайте возвращениялюбой
  4. Предположить исключения для ошибок
  5. Переименовать для ясности
  6. Вернуть конкретные типы или интерфейсы
  7. Используйте правильные абстракции
  8. Создать значимые объекты

Рефакторинг ⚙

https://hackernoon.com/code-refactoring-tips-no-015-remove-null?embedable=true

Контекст 💬

Когда вы пишете метод, который может вернуть много типов, напримерлюбойилинулевойВы теряете полиморфизм.

Полиморфизм позволяет обрабатывать объекты, которые совместно используют интерфейс или базовый тип взаимозаменяемо, упрощая ваш код.

Возвращение NULL заставляет ваших вызывающих абонентов писать дополнительные проверки и обрабатывать особые случаи, которые раскачивают код и увеличивают связь.

Возвращение любого (или типа, который стирает фактическую информацию типа), затрудняет понимание того, что на самом деле возвращает этот метод, вызывая ошибки и путаницу.

Вы вынуждаете абонентов выполнять проверку типов икастингПолем

Это нарушает фундаментальный принцип полиморфизма, где объекты должны вести себя в соответствии с ихконтрактыПолем

Методы должны возвращать конкретные типы, которые четко передают их намерения и позволяют компилятору проверить правильность во время компиляции.

Помнить

Два метода полиморфны, если их подписи одинаковы, аргументы полиморфны, а возвращение такжеПолиморфныйПолем

Пример кода 📖

Wrong ❌

public class DatabaseConnection {
    public Object execute(String sql) {
        if (sql.startsWith("SELECT")) {
            return new ResultSet();
        } else if (sql.startsWith("INSERT")) {
            return Integer.valueOf(42);
        } else if (sql.startsWith("UPDATE")) {
            return Boolean.TRUE;
        }
        return null;
        // The billion dollar mistake
    }
}

public class QueryHandler {
    public void handle(String sql, DatabaseConnection db) {
        Object result = db.execute(sql);
        // The caller needs to be aware of many different types
        if (result instanceof ResultSet) {
            System.out.println("Fetched rows");
        } else if (result instanceof Integer) {
            System.out.println("Inserted " + result);
        } else if (result instanceof Boolean) {
            System.out.println("Updated " + result);
        } else {
            System.out.println("Unknown result");
        }
    }
}

// This second class has a method execute()
// which is NOT polymorphic since it returns 
// another types
public class NonRelationalDatabaseConnection {
    public Object execute(String query) {
        if (query.startsWith("FIND")) {
            return new Document();
        } else if (query.startsWith("INSERT")) {
            return Integer.valueOf(1);
        } else if (query.startsWith("UPDATE")) {
            return Boolean.TRUE;
        }
        return null; // The billion dollar mistake
    }
}

Справа 👉

interface QueryResult {
    void display();
}

class SelectResult implements QueryResult {
    public void display() {
        System.out.println("Fetched rows");
    }
}

class InsertResult implements QueryResult {
    private final int count;
    InsertResult(int count) { this.count = count; }
    public void display() {
        System.out.println("Inserted " + count);
    }
}

class UpdateResult implements QueryResult {
    private final boolean ok;
    UpdateResult(boolean ok) { this.ok = ok; }
    public void display() {
        System.out.println("Updated " + ok);
    }
}

class DocumentResult implements QueryResult {
    public void display() {
        System.out.println("Fetched documents");
    }
}

interface DatabaseConnection {
    QueryResult execute(String query);
}

public class RelationalDatabaseConnection 
  implements DatabaseConnection {
    public QueryResult execute(String sql) {
        // execute() is now polymorphic and returns a QueryResult
        if (sql.startsWith("SELECT")) {
            return new SelectResult();
        } else if (sql.startsWith("INSERT")) {
            return new InsertResult(42);
        } else if (sql.startsWith("UPDATE")) {
            return new UpdateResult(true);
        }
        // You remove null
        throw new IllegalArgumentException("Unknown SQL");
    }
}

public class NonRelationalDatabaseConnection 
  implements DatabaseConnection {
    public QueryResult execute(String query) {
        // execute() is now polymorphic and returns a QueryResult
        if (query.startsWith("FIND")) {
            return new DocumentResult();
        } else if (query.startsWith("INSERT")) {
            return new InsertResult(1);
        } else if (query.startsWith("UPDATE")) {
            return new UpdateResult(true);
        }
        throw new IllegalArgumentException("Unknown query");
    }
}

public class QueryHandler {
    public void handle(String sql, DatabaseConnection db) {
        QueryResult result = db.execute(sql);
        result.display();
    }
}

Обнаружение 🔍

  • Полуавтоматический

Ищите методы с такими типами возврата, как объект, любая, void*или частые возвраты NULL.

Также проверьте на наличие рассеянных чеков IF-null или проверки типа после вызовов метода.

Инструменты и статические анализаторы иногда предупреждают о методах, возвращающих любую или нулевую без документации.

Поиск, например, проверки или тип кастинга после вызовов метода.

Следите за методами, которые возвращают разные типы на основе параметров или их внутреннего состояния.

Исключения 🛑

  • Общие каркасы коллекции
  • Библиотеки сериализации

Теги 🏷

  • Полиморфизм

Уровень 🔋

  • Средний

Почему биение важна 🗺

Когда метод всегда возвращает тип, который соответствует концепции, которую он представляет, программистам не нужны особые случаи.

Сломать этоБиениеВозвращение любой или NULL создает двусмысленность.

Призывник должен угадать фактический тип или дело с нулю, увеличивая ошибки и стоимость технического обслуживания.

Реальный мирОбъекты имеют определенные типы и поведение.

Поколение AI 🤖

Генераторы ИИ иногда производят методы, возвращающие любые или нулевые, потому что они определяют приоритеты гибкости или простоты над сильным набором и полиморфизмом.

Обнаружение ИИ 🧲

Инструменты искусственного интеллекта могут исправить этот запах, когда указаны инструкции по обеспечению применения набранных возвратов и предложитьНулевой объектили дополнительные шаблоны.

Они могут автоматически рефакторировать NULL возвращаться в полиморфные иерархии возврата.

Простые подсказки о «улучшении типов возврата» часто помогают ИИ предлагать лучшие альтернативы.

Попробуйте их! 🛠

Помните: помощники ИИ делают много ошибок

Предлагаемая подсказка: замените методы возвращающего объекта, любого или нулевого на конкретные типы возврата. Создайте правильные абстракции и модели NULL объектов. Обеспечить безопасность типа и четкие контракты

Без надлежащих инструкций

С конкретными инструкциями

Чатгпт

Чатгпт

Клод

Клод

Недоумение

Недоумение

Второй пилот

Второй пилот

Ты

Ты

Близнецы

Близнецы

DeepSeek

DeepSeek

Meta ai

Meta ai

Грок

Грок

Qwen

Qwen

Заключение 🏁

Методы должны возвращать конкретные типы, которые четко сообщают об их цели и позволяют проверять время компиляции.

Когда вы заменяете неполиморфный возврат на правильные абстракции, вы создаете более безопасный, более поддерживаемый код, который четко выражает его намерение.

Отношения 👩‍❤

https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-ix-7rr33ol

https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-iii-t7h3zkv

https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-iii-t7h3zkv

https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-ix-7rr33ol

https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-xxvi

Больше информации 📕

https://hackernoon.com/how-to-get-rid-of-annoying-ifs-forever-zuh3zlo?embedable=true

https://hackernoon.com/null-the-billion-dollar-mistake-8t5z32d6?embedable=true

https://en.wikipedia.org/wiki/design_by_contract?embedable=true

Отказ от ответственности 📘

Кодовые запахи - моимнениеПолем

Кредиты 🙏

ФотоРэнди ФатнаНеспособный


Верните правильный тип, всегда.

Брайан Гетц

https://hackernoon.com/400-thought-предвидение-software-engineering-quotes?embedable=true


Эта статья является частью серии Codesmell.

https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-i-xqz3evd?embedable=true


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE