Данные.
Если вы хотите быть слабосвязанными, ответ - данные. Ваши программы или компоненты не будут «выполнять функции» друг над другом, они будут передавать данные друг другу. (Внутренне, это, вероятно, будет сделано путем вызова метода - но механика в стороне, это должно быть, концептуально, передача данных).
Если данные внутри вашего процесса обрабатываются как неизменные, даже лучше.
Это дает четкие границы компонентов и в значительной степени защищает от изменений, происходящих на вас (поскольку вы можете относительно легко преобразовать одну форму данных в другую). Это также облегчает передачу в многопроцессорные или сетевые приложения, так как семантика передачи данных везде одинакова - нет предположения о магических изменениях скрытого состояния без передачи какого-либо нового фрагмента данных.
Четко определенные интерфейсы, которые передают данные, и вы великолепны. И я имею в виду данные, а не «объекты», которые знают, как делать всевозможные магические вещи. Просто простые, чистые данные.
Кстати, это не значит, что объектам нет места в вашей программе - это то, что действует на ваши данные.
Кроме того, убедитесь, что части вашей программы, которые работают с данными, отделены от частей программы, которые отвечают за ее фактическое перемещение, и частей, которые показывают ее пользователю. Я знаю, что все дело в том, чтобы снова «отделить свою бизнес-логику от ваших постоянных элементов и уровня пользовательского интерфейса».