Предположим, у меня есть пользовательский объект, студент :
public class Student{
public int _id;
public String name;
public int age;
public float score;
}
И класс Window , который используется для отображения информации об ученике :
public class Window{
public void showInfo(Student student);
}
Это выглядит вполне нормально, но я обнаружил, что Window не так-то просто проверить по отдельности, потому что для вызова функции нужен настоящий объект Student . Поэтому я пытаюсь изменить showInfo, чтобы он не принимал объект Student напрямую:
public void showInfo(int _id, String name, int age, float score);
чтобы было проще протестировать Window индивидуально:
showInfo(123, "abc", 45, 6.7);
Но я обнаружил, что модифицированная версия имеет другие проблемы:
Изменение Студента (например, добавление новых свойств) требует изменения метода-подписи showInfo
Если бы у Student было много свойств, метод-подпись Student был бы очень длинным.
Итак, если использовать пользовательские объекты в качестве параметра или принять каждое свойство в объектах в качестве параметра, какое из них более приемлемо?
showInfo
нужна настоящая строка, реальный float и два настоящих целых. Как обеспечить реальныйString
объект лучше, чем реальныйStudent
объект?int
параметра. С сайта вызова нет подтверждения, что вы действительно передаете их в правильном порядке. Что делать, если вы меняете местамиid
иage
, или,firstName
иlastName
? Вы вводите потенциальную точку отказа, которую очень сложно обнаружить, пока она не взорвется, и добавляете ее на каждом сайте вызовов .showForm(bool, bool, bool, bool, int)
метод - я люблю их ...Ответы:
Использование настраиваемого объекта для группировки связанных параметров на самом деле является рекомендуемым шаблоном. В качестве рефакторинга он называется « Ввести объект параметров» .
Ваша проблема лежит в другом месте. Во-первых, универсальный
Window
ничего не должен знать о студенте. Вместо этого вы должны иметь какой-то вид,StudentWindow
который знает только об отображенииStudents
. Во-вторых, нет абсолютно никаких проблем с созданиемStudent
экземпляра для тестирования,StudentWindow
если толькоStudent
он не содержит сложной логики, которая может значительно усложнить тестированиеStudentWindow
. Если у него естьStudent
такая логика, то создание интерфейса и насмешка должны быть предпочтительными.источник
Student
Группировка имеет смысл , и, скорее всего, возникают в других областях приложения.Student
, это будет весь объект Preservea.b.c
если твой метод беретa
. Если ваш метод доходит до того, что вам нужно иметь примерно более 4 параметров или 2 уровня глубины присоединения свойства, его, вероятно, необходимо учесть. Также обратите внимание, что это руководство - как и все другие рекомендации, оно требует усмотрения пользователя. Не следуй этому вслепую.Вы говорите, что это
Но вы можете просто создать объект ученика для передачи в ваше окно:
Это не кажется более сложным, чтобы позвонить.
источник
Student
ссылается на aUniversity
, который относится ко многимFaculty
s иCampus
s, сProfessor
s иBuilding
s, ни один из которых наshowInfo
самом деле не используется, но вы не определили какой-либо интерфейс, который позволяет тестам «знать» это и предоставлять только соответствующему ученику данные, без построения всей организации. ПримерStudent
представляет собой простой объект данных, и, как вы говорите, тесты должны быть рады работать с ним.С точки зрения непрофессионала:
Редактировать:
Как утверждает @ Tom.Bowen89, протестировать метод showInfo не намного сложнее:
источник
источник
Стив Макконнелл из Code Complete рассмотрел эту проблему, обсудив преимущества и недостатки передачи объектов в методы вместо использования свойств.
Простите, если я ошибаюсь в некоторых деталях, я работаю по памяти, так как прошло больше года с тех пор, как у меня был доступ к книге:
Он приходит к выводу, что лучше не использовать объект, а отправлять только те свойства, которые абсолютно необходимы для метода. Метод не должен знать ничего об объекте за пределами свойств, которые он будет использовать как часть своих операций. Кроме того, со временем, если объект когда-либо изменяется, это может иметь непредвиденные последствия для метода, использующего объект.
Также он обратился к тому, что если в итоге вы получите метод, который принимает множество различных аргументов, то это, вероятно, признак того, что метод делает слишком много и должен быть разбит на более мелкие методы.
Однако иногда, иногда вам действительно нужно много параметров. В качестве примера он привел бы метод, который строит полный адрес, используя множество различных свойств адреса (хотя это можно обойти, используя строковый массив, когда вы об этом думаете).
источник
Student
в данном случае). И именно так тестирование определяет дизайн , полностью принимая ответ с наибольшим количеством голосов при сохранении целостности дизайна.Тесты гораздо проще писать и читать, если вы пройдете весь объект:
Для сравнения,
Строка может быть написана, если вы передаете значения отдельно, как:
где фактический вызов метода похоронен где-то вроде
Дело в том, что фактический вызов метода в тесте невозможен - это признак того, что ваш API плох.
источник
Вы должны передать то, что имеет смысл, некоторые идеи:
Проще проверить. Если объект (ы) необходимо отредактировать, что требует наименьшего рефакторинга? Полезно ли повторно использовать эту функцию для других целей? Какое наименьшее количество информации мне нужно, чтобы эта функция выполнялась? (Разбивая его - он может позволить вам повторно использовать этот код - будьте осторожны, чтобы не упасть в конструктивную дыру создания этой функции, а затем ограничить все возможности исключительно для использования этого объекта.)
Все эти правила программирования - всего лишь руководство, которое поможет вам мыслить в правильном направлении. Просто не создавайте зверя с кодом - если вы не уверены и вам просто нужно продолжить, выберите направление / ваше собственное или предложение здесь, и если вы достигли точки, когда вы думаете: «О, я должен был сделать это так» путь »- тогда вы, вероятно, сможете вернуться и довольно легко изменить его. (Например, если у вас есть класс Teacher - ему просто нужно установить то же свойство, что и для Student, и вы измените свою функцию для принятия любого объекта формы Person)
Я был бы наиболее склонен к тому, чтобы основной объект передавался - потому что, как я кодирую, легче объяснить, что делает эта функция.
источник
Один из распространенных способов обойти это - вставить интерфейс между двумя процессами.
Иногда это становится немного грязно, но в Java все становится немного чище, если вы используете внутренний класс.
Затем вы можете проверить
Window
класс, просто предоставив ему поддельныйHasInfo
объект.Я подозреваю, что это пример шаблона декоратора .
добавленной
Кажется, есть некоторая путаница, вызванная простотой кода. Вот еще один пример, который может продемонстрировать технику лучше.
источник
Student
иString
здесь для типа возврата только для демонстрации. Скорее всего, будут дополнительные параметры,getInfo
например,Pane
для рисования при рисовании. Идея заключается в том, чтобы передать функциональные компоненты в качестве декораторов исходного объекта .HasInfo
объектах.Student
знает как быть.getInfo
return void, передайте его aPane
для рисования, тогда реализация (вStudent
классе) внезапно соединится с swing или чем-то, что вы используете. Если вы заставите его вернуть некоторую строку и принять 0 параметров, то ваш пользовательский интерфейс не будет знать, что делать со строкой без магических предположений и неявного связывания. Если выgetInfo
действительно вернете некоторую модель представления с соответствующими свойствами, тогда вашStudent
класс снова будет связан с логикой представления. Я не думаю, что любая из этих альтернатив желательнаУ вас уже есть много хороших ответов, но вот еще несколько предложений, которые могут позволить вам увидеть альтернативное решение:
Ваш пример показывает, что Student (явно объект модели) передается в Window (очевидно, объект уровня представления). Промежуточный объект Controller или Presenter может быть полезен, если у вас его еще нет, что позволяет вам изолировать ваш пользовательский интерфейс от вашей модели. Контроллер / презентатор должен предоставлять интерфейс, который можно использовать для его замены для тестирования пользовательского интерфейса, и должен использовать интерфейсы для обращения к объектам модели и просмотра объектов, чтобы иметь возможность изолировать его от обоих для тестирования. Вам может понадобиться какой-то абстрактный способ их создания или загрузки (например, объекты Factory, объекты Repository или аналогичные).
Перенос соответствующих частей ваших объектов модели в объект передачи данных является полезным подходом для взаимодействия, когда ваша модель становится слишком сложной.
Может случиться так, что ваш ученик нарушает принцип разделения интерфейса. Если это так, может быть полезно разделить его на несколько интерфейсов, с которыми проще работать.
Ленивая загрузка может упростить построение больших графов объектов.
источник
Это на самом деле достойный вопрос. Настоящая проблема здесь заключается в использовании общего термина «объект», который может быть немного двусмысленным.
Как правило, в классическом языке ООП термин «объект» стал означать «экземпляр класса». Экземпляры классов могут быть довольно тяжелыми - общедоступные и частные свойства (и промежуточные), методы, наследование, зависимости и т. Д. На самом деле вы не захотите использовать что-то подобное для простой передачи некоторых свойств.
В этом случае вы используете объект в качестве контейнера, который просто содержит некоторые примитивы. В C ++ такие объекты были известны как
structs
(и они все еще существуют в таких языках, как C #). Структуры, на самом деле, были разработаны именно для использования, о котором вы говорите - они группировали связанные объекты и примитивы вместе, когда у них были логические отношения.Однако в современных языках нет никакой разницы между структурой и классом, когда вы пишете код , так что вы можете использовать объект. (Однако за кулисами есть некоторые отличия, о которых вы должны знать - например, структура - это тип значения, а не ссылочный тип.) В основном, если вы сохраняете свой объект простым, это будет легко проверить вручную. Современные языки и инструменты позволяют вам немного смягчить это (через интерфейсы, макеты, внедрение зависимостей и т. Д.)
источник
Student
) в модели представлений (StudentInfo
илиStudentInfoViewModel
т. Д.), Но это может быть необязательно.